github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/docs/design/2020-03-04-ticdc-design-and-architecture-cn.md (about) 1 # 系统架构 2 3 TiCDC 是一款通过拉取 TiKV 变更日志实现的 TiDB 增量数据同步工具。具有还原数据到与上游任意 TSO 一致状态的能力,同时提供开放数据协议,支持其他系统订阅数据变更。 4 5 TiCDC 集群由多个无状态节点构成,通过 PD 内部的 etcd 实现高可用。集群支持创建多个同步任务,向多个不同的下游进行数据同步。TiCDC 的系统架构如下图所示: 6 7 <img src="../media/cdc_architecture.svg?sanitize=true" alt="architecture" width="600"/> 8 9 ## 系统组件 10 11 - TiKV:只输出 kv change logs 12 13 - 内部逻辑拼装 kv change log 14 - 提供输出 kv change logs 的接口,发送数据包括实时 change logs 和增量扫的 change logs 15 16 - Capture:TiCDC 运行进程,多个 capture 组成一个 TiCDC 集群,负责 kv change log 的同步 17 - 每个 capture 负责一部分的 kv change logs 拉取 18 - 对拉取的一个或多个 kv change log 进行排序 19 - 向下游还原事务或按照 TiCDC open protocol 进行输出 20 21 ## 逻辑概念 22 23 - KV change log:TiKV 提供的隐藏大部分内部实现细节的的 row changed event 24 - TiCDC open protocol:通过 MQ 对外提供的开放 TiCDC 协议,协议输出的 cdc log 具备以下两种处理能力 25 26 - 按照 row 顺序更新目标系统 27 - 还原等价事务逻辑同步到目标系统 28 29 - Capture:一个 TiCDC 节点独立运行时的逻辑抽象 30 - Owner:一种 capture 的角色,每个 TiCDC 集群同一时刻最多只存在一个 capture 具有 owner 角色,负责集群内部的调度 31 - Processor:capture 内部的逻辑线程,每个 processor 负责处理同一个同步流中一个或多个 table 的数据。一个 capture 节点可以运行多个 processor 32 - ChangeFeed:一个由用户启动的从上游 TiDB 同步到下游的任务,其中包含多个 task,task 会分布在不同的 capture 节点进行数据同步处理 33 34 # 模块设计 35 36 ## TiKV 接口 37 38 ### EventFeed rpc 39 40 TiKV 对 TiCDC 提供一个隐藏大部分内部实现细节的的 row changed event 的接口,该接口会扫描指定 region 的 kv range,通过 gRPC stream 持续返回 row changed event。协议的细节可以参考 [kvproto/cdcpb.proto](https://github.com/pingcap/kvproto/blob/master/proto/cdcpb.proto)。以下会分别描述建立数据流后 TiCDC 和 TiKV 的处理行为。 41 42 ``` 43 service ChangeData { rpc EventFeed(ChangeDataRequest) returns(stream ChangeDataEvent);} 44 ``` 45 46 ### TiCDC 处理增量扫和实时数据流 47 48 TiCDC 使用 EventFeed 接口获取 ts 之后的数据的流程是: 49 50 1. 指定 ts 建立 EventFeed stream,接口返回的实时数据先在 TiCDC 内部缓存。(EventFeed stream 会返回 P + C,所以建立 stream 的时候应该要先扫下还没提交的数据,不然可能后续只拿到个 C 没有对应的 value)。 51 2. 在 EventFeed 接口持续获取 raftstore 的 snapshot 扫出的 ts 后的数据变更, 拿到的信息包括:(key, value, Put/Delete, ts)。(如果对应的 row kv 已经 commited,返回 commited kv record,如果没有 commited,需要返回 prewrite record),这一步就是增量扫的环节。 52 3. 增量扫完成时,会收到 INITIALIZED 类型的 event,TiCDC 继续处理 EventFeed 返回的实时数据。 53 54 ### TiKV 具体行为 55 56 TiKV 建立 TiCDC 链接后的行为分为两个阶段: 57 58 1. 增量扫。在这个阶段,TiKV 会根据 TiCDC 请求中的 checkpoint ts 进行增量扫,输出的内容有: 59 1. Prewrite,发生在 (checkpoint ts, current ts] 的上锁记录 60 2. Committed,发生在 (checkpoint ts, current ts] 的提交记录,即 prewrite + commit 之后的结果 61 3. Initialized,代表增量扫完成,后续不会有 committed 内容输出 62 2. 推流。这个阶段贯穿于整个 TiCDC 链接生命周期,将 region 上的写入实时推到下游,输出的内容有: 63 1. Prewrite,region 上的上锁操作 64 2. Commit,region 上的提交操作 65 3. ResolvedTS,一个特殊的 TS,保证该 region 后续不会再出现小于该 TS 的提交操作 66 67 注意这两个阶段是同时开始的(即建立连接)。 68 69 ![tikv cdc stream init](../media/tikv_cdc_stream_init.png) 70 71 ### KV event 72 73 接口返回的 Event 包含 4 种类型,分别是: 74 75 - Entries:通常的 KV log event,对应的 log 类型有以下 6 种: 76 77 - UNKNOWN:未知的 log 类型 78 - PREWRITE:TiKV 在 2PC prewrite 阶段写入的数据 79 - COMMITT,TiKV 在 2PC commit 阶段最后 commit 的数据 80 - ROLLBACK,TiKV 在 2PC commit 阶段发生 rollback 的数据 81 - COMMITED,代表增量扫过程中遇到的 commited 的内容 82 - INITIALIZED,代表增量扫完成 83 84 - Admin:目前 TiCDC 中不需要处理这种类型的 event 85 - Error:接口出现错误是返回该类型 event,包括以下 3 种错误 86 87 - errorpb.NotLeader 88 - errorpb.RegionNotFound 89 - errorpb.EpochNotMatch,region 分裂后通常会在已经建立的 gRPC stream 中返回此错误,TiCDC 遇到该错误时会重新扫描 kv range 获取新的 region 信息,并重新建立新的同步数据流。 90 91 - ResolvedTs:为了数据还原的一致性,只有当所有 region 都保证**在某个 ts 之前的所有数据都已经被 TiCDC 获取到**,TiCDC 才会对 ts 前的 kv change event 进行排序并向下游进行事务还原。 因此对于一定时间没有任何数据写入的 region,需要提供某种机制推进该 ts,以降低 TiCDC 还原事务的延迟。ResolvedTs 类型 event 就是解决这个问题。 92 93 ## Capture 组件 94 95 Capture 运行过程中各组件需要持久化的数据,包括同步任务的配置,同步任务的同步状态,capture 的信息,owner 的 leader 选举信息,processor 的同步状态信息,目前都存储在 etcd 中。Capture 内部各组件的逻辑关系如图所示: 96 97 ![capture architecture](../media/capture_architecture.png) 98 99 ### Owner 选举策略 100 101 1. 每个 capture 节点启动时生成一个 UUID 作为 Capture ID,并向 etcd 注册 capture 信息 102 2. 每个 capture 会使用 [Election](https://godoc.org/github.com/coreos/etcd/clientv3/concurrency#Election) 参与 owner 选举,最多只有一个 capture 节点会选举成功成为 owner 103 104 ### Owner 角色 105 106 Owner 维护全局的同步状态,会对集群的同步进行监控和适当的调度,owner 运行有以下逻辑: 107 108 - table 同步调度 109 110 - 调度 table 的同步任务,分发到某个节点或从某个节点删除 111 - 维护运行的 processor 状态信息,对于异常节点进行清理 112 113 - 执行处理 DDL,向下游同步 DDL 114 - 更新每一个 changefeed 的全局的 CheckpointTs 和 ResolvedTs 115 116 ### Processor 角色 117 118 当 capture watch etcd 发现有新分配的 changefeed 同步任务到自己节点上时,会创建一个新的 processor 来进行数据同步 ,每个 processor 运行有以下逻辑: 119 120 - 建立 EventFeed gRPC stream 拉取 kv change event 121 122 - processor 负责同步哪些表由 owner 调度,表的调度信息储存在 etcd 中 123 - processor 创建时会读取被分配了哪些表进行同步,将这些表的 table id 按照 TiDB 内部的 key 编码逻辑进行编码得到一些需要拉取 kv change log 的 key range,processor 会综合 key range 和 region 分布信息创建多个 EventFeed gRPC stream。 124 - processor 运行过程中也会监控同步信息,对于增加或删除的表调整 EventFeed gRPC stream。 125 126 - 维护本地 ResovledTs 和 CheckpointTs 127 - 根据全局 ResolvedTs 推进自己节点的数据向下游同步 128 129 ### DML 和 DDL 的正确性保证 130 131 - 全局 ResolvedTs 由 owner 计算,取值为所有 processor 的 ResolvedTs 和 DDL puller 的 ResolvedTs 的最小值 132 - 所有 processor 严格按照全局 ResolvedTs 进行数据同步,只会同步数据到全局 ResolvedTs。同步数据后会更新本地 CheckpointTs 133 - 同步 DDL 前需要保证所有 processor 的 CheckpointTs 到达 DDL commitTs,之后才会向下游执行 DDL 134 135 # 高可用策略 136 137 目前高可用采用的策略如下所示: 138 139 - 集群内有且仅有一个 owner 角色 140 - owner 出现异常后可以在其他 capture 节点自动选举出新 owner 141 - owner 负责 table 同步的调度,采用 etcd 统一存储状态来进行调度,owner 将调度结果直接写入 etcd,processor 按照状态进行对应行为 142 - processor 所在节点出现异常后集群会将 table 调度到其他节点 143 144 # TiCDC open protocol 145 146 设计细节:[TiCDC Kafka 同步协议](2020-02-24-ticdc-mq-protocol-cn.md) 147 148 # 使用限制 149 150 同步到 TiDB/MySQL,需要满足以下条件才可以保证正确性 151 152 - 表必须要有主键或者唯一索引 153 - 如果只存在唯一索引,至少有一个唯一索引的每一列在表结构中明确定义 NOT NULL