关于java实现tidb CDC的二三事

转载请注明原文地址https://www.jianshu.com/p/b86cc9354b20

TiCDC拥有自身的分布式高可用设计,且这些设计依赖数据库实例的PD组件,所以从这个角度来看,相对于debezium这种小工具,可能要稍微复杂一些。对于实现TiDB的Change Data Capture,并不是一个很新的话题,除了PingCAP原厂支持的binlog, cdc以外,业界早几年前就有过相关探索。
上周Flink CDC宣布在即将推出的2.2版本中新增tidb的cdc connector,当时就在想,如果是java版的tidb cdc,很有可能从TiFlink这个项目中衍生……今天看到pr了,居然真的是借鉴了TiFlink的代码……
https://github.com/ververica/flink-cdc-connectors/commit/b911dd2a1b884d728393bd95e2a5966c8b878bb8

TiFlink与TiCDC

TiFlink是2020年TiDB Hackathon大赛的一个著名项目,使用 TiKV 和 Flink 实现了强一致的物化视图的功能。
其内部并未采用TiCDC,而是直接调用了TiKV的CDC接口,自己用java实现接收了TiKV推送过来的cdc日志并组装,并将该功能与flink集成,使得flink拥有了抽取tidb变化数据的能力。

首先,作为渣渣的我先膜拜一下参赛大佬,能在极短的时间内开发出这样一套工具并演说出来,真的不简单,NB……orz

但是,从客观事实角度来讲,短时间开发的experimental项目,功能稳定性,与经历过生产考验的TiCDC对比,这个结果是毫无疑问的,不用说了吧。

两者共同点

都是grpc通信,都是通过TiKV侧的CDC接口来接收TiKV推送过来的cdc日志,组装成完整的事务。
我之前有一篇文章专门介绍TiKV侧CDC接口:
https://www.jianshu.com/p/dd5c7c222703

不同点,或者说:基于TiFlink二次开发,有哪些核心功能需要完善的?

以下都是ticdc都已经具备的功能,但tiflink缺少的。我先按功能重要性列了个表格,其中:
★ 表示必要功能,缺少将不能投产
☆ 表示重要功能,缺少将严重影响运维、很多场景会出现问题

功能点 重要性
对table id变化的处理
对region状态变化的处理
对GC safepoint的考虑
对表结构变化的支持
对大事务的支持
支持分区表 一般
  1. 对table id变化的处理
    在tidb中,每张表都对应有一个table id。我们想抽取这张表的数据,首先需要通过table id计算出这张表对应的key区间,然后去pd里面找这个区间对应的region,再向对应region发起请求。
key区间的编码规则为:
从 t<table id>_r 至 t<table id>_s 的左闭右开
然后编码为8+1字节对齐,便于memcmp,具体规则如下
// EncodeBytes guarantees the encoded value is in ascending order for comparison,
// encoding with the following rule:
//  [group1][marker1]...[groupN][markerN]
//  group is 8 bytes slice which is padding with 0.
//  marker is `0xFF - padding 0 count`
// For example:
//   [] -> [0, 0, 0, 0, 0, 0, 0, 0, 247]
//   [1, 2, 3] -> [1, 2, 3, 0, 0, 0, 0, 0, 250]
//   [1, 2, 3, 0] -> [1, 2, 3, 0, 0, 0, 0, 0, 251]
//   [1, 2, 3, 4, 5, 6, 7, 8] -> [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247]
// Refer: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format#memcomparable-format
举个例子,我建了一张表,table id为45,那么这张表的区间范围就是:
7480000000000000ff2d5f720000000000fa
至
7480000000000000ff2d5f730000000000fa

先不要去纠结key怎么编码的,这里我想说的是table id会发生变化。当对一张表执行某些特定的ddl语句时,比如truncate,table id一定会发生变化。一旦table id发生变化,还用原先计算的key区间去订阅region,明显获取不到你想要的数据的。
我们看下ticdc是怎么处理这个问题的:
其实很简单,只需要订阅ddl对应的region即可,如果发现有对这张表的ddl会造成table id变化,取消原来的订阅,根据新的table id重新计算key区间,发起新的订阅。

tidb的元数据也存储于tikv内,对应的key区间编码规则为:
从 m<元数据属性名>l 至 m<元数据属性名>m 的左闭右开
比如ddl对应的元数据属性名为 "DDLJobList"
订阅这类区间就可以获得所有ddl操作。
  1. 对region状态变化的处理
    之前就已经说过,TiKV的region不是固定不变的,可能发生分裂、合并、易主等状况,尤其是在生产环境,region变化是会比较频繁的,如果不处理此类状态变化,明显是不能用于生产的。
    region常见状态变化,来自cdcpb.proto:
message Error {
    // 某tikv节点比较繁忙可能出现,另一个tikv节点被选为了region的leader
    errorpb.NotLeader not_leader = 1;
    // region发生合并的常见异常
    errorpb.RegionNotFound region_not_found = 2;
    // region发生分裂的常见异常
    errorpb.EpochNotMatch epoch_not_match = 3;
    DuplicateRequest duplicate_request = 4;
    Compatibility compatibility = 5;
    ClusterIDMismatch cluster_id_mismatch = 6;
}

当收到region状态变化的通知时,需要重新去pd获取当前region信息。不过有一点需要注意,pd的region信息可能也不是最新的,获取到之后还要进行检查,region是否cover了你订阅的key区间。

可以通过以下命令手动迫使region分裂合并,观察ticdc的行为:
分裂:
mysql -h xxx -u root -P 4000 -e "split table cdc_bench.sbtest1 between (0) and (10000) regions 2"
合并:
pd-ctl --pd=http://x.x.x.x:2379 operator add merge-region 528 2
  1. 对GC safepoint的考虑
    注意这里说的gc不是jvm gc,是指TiDB内部对于MVCC的过期数据的一个定期清理过程。众所周知MVCC并不是把update和delete的数据覆盖掉,而是新建立一个版本。如果不定期做清理,那么历史版本会越积越多,影响数据库的读性能。所以GC safepoint是指某个时间点,早于这个时间点的所有历史数据都将被清理掉;TiDB负责定期推进这个时间点,TiKV负责按时间点来执行清理操作。
    TiCDC问世以后,由于TiCDC宕机与重启这期间的数据完全靠TiKV的“增量扫”来获取,如果历史数据被清理掉,增量扫是无法获取到正确的值(至少delete操作会丢失)。故TiCDC也有一个GC safepoint,只有正常运行时,这个safepoint才会往前推进。TiKV综合TiDB的safepoint和cdc的safepoint来进行GC操作,以此保证不会将不该清理的数据清理掉。
    GC safepoint有一个专门的rpc接口定义在pdpb.proto
// TiDB的
    rpc UpdateGCSafePoint(UpdateGCSafePointRequest) returns (UpdateGCSafePointResponse) {}
// TiCDC的
    rpc UpdateServiceGCSafePoint(UpdateServiceGCSafePointRequest) returns (UpdateServiceGCSafePointResponse) {}
  1. 对表结构变化的支持
    从TiKV接口中获取的rowdata是schemaless的,并不包含表结构信息。所以要想将DML数据完整还原出来,必须获取该条rowdata对应的正确版本的schema。
    但是很不幸,和binlog不同,TiKV的CDC接口并未提供这一行数据对应的schema版本号。
    所以这个问题不太好处理。并且由于TiDB是分布式数据库,其对于Online DDL的支持会比单机数据库要复杂许多,变相增加了处理难度。
    TiCDC对于这块的处理主要是以时间戳和因果关系为原则,兼具考虑Online DDL的所有中间态,逻辑比较复杂,这里不做详细展开。
  2. 对大事务的支持
    这个就不用细说了,因为要基于prewrite和commit操作来进行事务组装,如果事务很大的话,内存可能装不下。TiCDC里使用了外排,组件名称是叫UnifiedSorter,大家可以自行看下。TiFlink则直接使用的hashmap来装,对大事务的支持有限。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容