title | summary |
---|---|
TiCDC 常见问题解答 |
了解 TiCDC 相关的常见问题。 |
本文档总结了使用 TiCDC 时经常遇到的问题。
注意:
本文档
cdc cli
命令中指定 server 地址为--server=http://127.0.0.1:8300
,在使用时你需要根据实际地址进行替换。
首先需要理解同步任务的 start-ts
对应于上游 TiDB 集群的一个 TSO,同步任务会从这个 TSO 开始请求数据。所以同步任务的 start-ts
需要满足以下两个条件:
start-ts
的值需要大于 TiDB 集群当前的tikv_gc_safe_point
,否则创建任务时会报错。- 启动任务时,需要保证下游已经具有
start-ts
之前的所有数据。对于同步到消息队列等场景,如果不需要保证上下游数据的一致,可根据业务场景放宽此要求。
如果不指定 start-ts
或者指定 start-ts=0
,在启动任务的时候会去 PD 获取一个当前 TSO,并从该 TSO 开始同步。
在使用 cdc cli changefeed create
创建同步任务时会检查上游表是否符合同步要求。如果存在表不满足同步限制,会提示 some tables are not eligible to replicate
并列出这些不满足的表。如果选择 Y
或 y
则会继续创建同步任务,并且同步过程中自动忽略这些表的所有更新。如果选择其他输入,则不会创建同步任务。
可以使用 cdc cli
查询同步任务的状态。例如:
cdc cli changefeed list --server=http://127.0.0.1:8300
上述命令输出如下:
[{
"id": "4e24dde6-53c1-40b6-badf-63620e4940dc",
"summary": {
"state": "normal",
"tso": 417886179132964865,
"checkpoint": "2020-07-07 16:07:44.881",
"error": null
}
}]
checkpoint
:即为 TiCDC 已经将该时间点前的数据同步到了下游。state
为该同步任务的状态,状态的值和含义参考 TiCDC 同步任务状态。
注意:
该功能在 TiCDC 4.0.3 版本引入。
在上游 TiDB 集群停止更新后,可以通过比较上游 TiDB 集群的最新 TSO 时间戳与 TiCDC 当前的同步进度判断同步是否完成。如果 TiCDC 的同步进度时间大于或等于上游 TiDB 集群的 TSO,则说明 TiCDC 已同步所有更新。具体操作步骤如下:
-
获取上游 TiDB 集群的最新 TSO 时间戳。
注意:
请使用
TIDB_CURRENT_TSO()
函数获取 TSO,而不是NOW()
等查询当前时间的函数。以下示例使用
TIDB_PARSE_TSO()
将 TSO 转换为可读的时间格式,便于后续比较:BEGIN; SELECT TIDB_PARSE_TSO(TIDB_CURRENT_TSO()); ROLLBACK;
输出结果示例如下:
+------------------------------------+ | TIDB_PARSE_TSO(TIDB_CURRENT_TSO()) | +------------------------------------+ | 2024-11-12 20:35:34.848000 | +------------------------------------+
-
获取 TiCDC 当前的同步进度。
你可以通过以下两种方法之一检查当前的同步进度:
-
方法一:查询同步任务的 Checkpoint(推荐)
使用 TiCDC 命令行工具
cdc cli
查看所有同步任务的 Checkpoint:cdc cli changefeed list --server=http://127.0.0.1:8300
输出结果示例如下:
[ { "id": "syncpoint", "namespace": "default", "summary": { "state": "normal", "tso": 453880043653562372, "checkpoint": "2024-11-12 20:36:01.447", "error": null } } ]
在输出结果中,
"checkpoint": "2024-11-12 20:36:01.447"
表示 TiCDC 当前的同步进度为2024-11-12 20:36:01.447
,即所有在该时间之前的上游 TiDB 变更已同步完成。如果该时间戳大于或等于步骤 1 中获取的上游 TiDB TSO 时间,则表示所有更新已经同步到下游。 -
方法二:查询下游 TiDB 的 Syncpoint 信息
如果下游是 TiDB,并且已启用 TiCDC Syncpoint 功能,可以通过查询下游 TiDB 的 Syncpoint 信息获取同步进度。
注意:
Syncpoint 信息的更新间隔由
sync-point-interval
参数控制。如需获取最新同步进度,建议使用方法一。在下游 TiDB 中执行以下 SQL 语句,获取上游 TSO (
primary_ts
) 和下游 TSO (secondary_ts
) 信息。SELECT * FROM tidb_cdc.syncpoint_v1;
输出结果示例如下:
+------------------+------------+--------------------+--------------------+---------------------+ | ticdc_cluster_id | changefeed | primary_ts | secondary_ts | created_at | +------------------+------------+--------------------+--------------------+---------------------+ | default | syncpoint | 453879870259200000 | 453879870545461257 | 2024-11-12 20:25:01 | | default | syncpoint | 453879948902400000 | 453879949214351361 | 2024-11-12 20:30:01 | | default | syncpoint | 453880027545600000 | 453880027751907329 | 2024-11-12 20:35:00 | +------------------+------------+--------------------+--------------------+---------------------+
在输出结果中,每一行表示上游 TiDB 在
primary_ts
时刻的 snapshot 与下游 TiDB 在secondary_ts
时刻的 snapshot 一致。要查看同步进度,可以将最新的
primary_ts
转换为可读的时间格式:SELECT TIDB_PARSE_TSO(453880027545600000);
转换结果示例如下:
+------------------------------------+ | TIDB_PARSE_TSO(453880027545600000) | +------------------------------------+ | 2024-11-12 20:35:00 | +------------------------------------+
如果
primary_ts
对应的时间大于或等于步骤 1 中获取的上游 TiDB 集群的 TSO 时间戳,说明 TiCDC 已经将所有更新同步到下游。
-
从 TiDB v4.0.0-rc.1 版本起,PD 支持外部服务设置服务级别 GC safepoint。任何一个服务可以注册更新自己服务的 GC safepoint。PD 会保证任何晚于该 GC safepoint 的 KV 数据不会在 TiKV 中被 GC 清理掉。
在 TiCDC 中启用了这一功能,用来保证 TiCDC 在不可用、或同步任务中断情况下,可以在 TiKV 内保留 TiCDC 需要消费的数据不被 GC 清理掉。
启动 TiCDC server 时可以通过 gc-ttl
指定 GC safepoint 的 TTL,也可以通过 TiUP 修改 TiCDC 的 gc-ttl
,默认值为 24 小时。在 TiCDC 中这个值有如下两重含义:
- 当 TiCDC 服务全部停止后,由 TiCDC 在 PD 所设置的 GC safepoint 保存的最长时间。
- 当 TiCDC 的 GC safepoint 阻塞 TiKV GC 数据时,
gc-ttl
表示 TiCDC 同步任务的最大同步延迟,若同步任务延迟超过gc-ttl
所设置的值,那么该同步任务就会进入failed
状态,并报ErrGCTTLExceeded
错误,无法被恢复,不再阻塞 GC safepoint 推进。
以上第二种行为是在 TiCDC v4.0.13 版本及之后版本中新增的。目的是为了防止 TiCDC 中某个同步任务停滞时间过长,导致上游 TiKV 集群的 GC safepoint 长时间不推进,保留的旧数据版本过多,进而影响上游集群性能。
注意:
在某些应用场景中,比如使用 Dumpling/BR 全量同步后使用 TiCDC 接增量同步时,默认的
gc-ttl
为 24 小时可能无法满足需求。此时应该根据实际情况,在启动 TiCDC server 时指定gc-ttl
的值。
TiCDC 服务启动后,如果有任务开始同步,TiCDC owner 会根据所有同步任务最小的 checkpoint-ts 更新到 PD service GC safepoint,service GC safepoint 可以保证该时间点及之后的数据不被 GC 清理掉。如果 TiCDC 中某个同步任务中断、或者被用户主动停止,则该任务的 checkpoint-ts 不会再改变,PD 对应的 service GC safepoint 最终会停滞在该任务的 checkpoint-ts 处不再更新。
如果该同步任务停滞的时间超过了 gc-ttl
指定的时长,那么该同步任务就会进入 failed
状态,并且无法被恢复,PD 对应的 service GC safepoint 就会继续推进。
TiCDC 为 service GC safepoint 设置的默认存活有效期为 24 小时,即 TiCDC 服务中断 24 小时内恢复能保证 TiCDC 继续同步所需的数据不因 GC 而丢失。
- 通过
cdc cli changefeed query
查询同步任务的错误信息,尽快修复错误。 - 调大
gc-ttl
的值,给修复错误留出时间,确保错误修复后不会因为同步延迟超过gc-ttl
而导致同步任务进入failed
状态。 - 在评估系统影响后,调大 TiDB 的 tidb_gc_life_time 的值以阻止 GC、保留数据,确保错误修复后不会因为 GC 清理数据而导致同步任务进入
failed
状态。
上游时区 | TiCDC 时区 | 下游时区 | |
---|---|---|---|
配置方式 | 见时区支持 | 启动 ticdc server 时的 --tz 参数 |
sink-uri 中的 time-zone 参数 |
说明 | 上游 TiDB 的时区,影响 timestamp 类型的 DML 操作和与 timestamp 类型列相关的 DDL 操作。 | TiCDC 会将假设上游 TiDB 的时区和 TiCDC 时区配置相同,对 timestamp 类型的列进行相关处理。 | 下游 MySQL 将按照下游的时区设置对 DML 和 DDL 操作中包含的 timestamp 进行处理。 |
注意:
请谨慎设置 TiCDC server 的时区,因为该时区会用于时间类型的转换。上游时区、TiCDC 时区和下游时区应该保持一致。TiCDC server 时区使用的优先级如下:
- 最优先使用
--tz
传入的时区。- 没有
--tz
参数,会尝试读取TZ
环境变量设置的时区。- 如果还没有
TZ
环境变量,会从 TiCDC server 运行机器的默认时区。
在使用 cdc cli changefeed create
命令时如果不指定 --config
参数,TiCDC 会按照以下默认行为创建同步任务:
- 同步所有的非系统表
- 只同步包含有效索引的表
支持。注意:对于 Canal 协议,TiCDC 只支持 JSON 输出格式,对 protobuf 格式尚未提供官方支持。要开启 Canal 协议的输出,只需在 --sink-uri
配置中指定 protocol
为 canal-json
即可。例如:
cdc cli changefeed create --server=http://127.0.0.1:8300 --sink-uri="kafka://127.0.0.1:9092/cdc-test?kafka-version=2.4.0&protocol=canal-json" --config changefeed.toml
注意:
- 该功能在 TiCDC 4.0.2 版本引入。
- 目前 TiCDC 仅支持将 Canal-JSON 格式的变更数据输出到 MQ 类的 Sink(例如 Kafka)。
更多信息请参考 TiCDC Changefeed 配置参数。
- 请参考如何查看 TiCDC 同步任务的状态?检查下同步任务的状态是否正常。
- 请适当调整 Kafka 的以下参数:
message.max.bytes
,将 Kafka 的server.properties
中该参数调大到1073741824
(1 GB)。replica.fetch.max.bytes
,将 Kafka 的server.properties
中该参数调大到1073741824
(1 GB)。fetch.message.max.bytes
,适当调大consumer.properties
中该参数,确保大于message.max.bytes
。
对于 Avro 和 Canal-JSON 格式,消息是以行变更为单位发送的,一条 Kafka Message 仅包含一条行变更。一般情况下,消息的大小不会超过 Kafka 单条消息上限,因此,一般不需要限制单条消息大小。如果单条 Kafka 消息大小确实超过 Kafka 上限,请参考为什么 TiCDC 到 Kafka 的同步任务延时越来越大。
对于 Open Protocol 格式,一条 Kafka Message 可能包含多条行变更。因此,有可能存在某条 Kafka Message 消息过大。可以通过 max-message-bytes
控制每次向 Kafka broker 发送消息的最大数据量(可选,默认值 10 MB),通过 max-batch-size
参数指定每条 kafka 消息中变更记录的最大数量(可选,默认值 16
)。
不会,在进行事务操作时,对于在一个事务内多次修改同一行的情况,TiDB 仅会将最新一次的修改结果发送给 TiKV。因此 TiCDC 仅能获取到最新一次修改的结果。
会,一条消息中可能出现多个 update
或 delete
,update
和 delete
也有可能同时存在。
这些信息包含在 Kafka 消息的 Key 中,比如:
{
"ts":<TS>,
"scm":<Schema Name>,
"tbl":<Table Name>,
"t":1
}
更多信息请参考 Open protocol Event 格式定义
把 Kafka 消息的 Key 中的 ts
右移 18 位即得 unix timestamp。
Open protocol 的输出中 type = 6 即为 null,比如:
类型 | Code | 输出示例 | 说明 |
---|---|---|---|
Null | 6 | {"t":6,"v":null} |
更多信息请参考 Open protocol Event 格式定义。
- 如果同时存在
"p"
和"u"
字段为UPDATE
事件 - 如果只存在
"u"
字段则为INSERT
事件 - 如果只存在
"d"
字段则为DELETE
事件
更多信息请参考 Open protocol Row Changed Event 格式定义。
TiCDC 使用 PD 内部的 etcd 来存储元数据并定期更新。因为 etcd 的多版本并发控制 (MVCC) 以及 PD 默认的 compaction 间隔是 1 小时,TiCDC 占用的 PD 存储空间与 1 小时内元数据的版本数量成正比。在 v4.0.5、v4.0.6、v4.0.7 三个版本中 TiCDC 存在元数据写入频繁的问题,如果 1 小时内有 1000 张表创建或调度,就会用尽 etcd 的存储空间,出现 etcdserver: mvcc: database space exceeded
错误。出现这种错误后需要清理 etcd 存储空间,参考 etcd maintenance space-quota。如果你的 TiCDC 版本为 v4.0.5、v4.0.6 或 v4.0.7,建议升级到 v4.0.9 及以后版本。
TiCDC 对大事务(大小超过 5 GB)提供部分支持,根据场景不同可能存在以下风险:
- 可能导致主从同步延迟大幅增高。
- 当 TiCDC 内部处理能力不足时,可能出现同步任务报错
ErrBufferReachLimit
。 - 当 TiCDC 内部处理能力不足或 TiCDC 下游吞吐能力不足时,可能出现内存溢出 (OOM)。
从 v6.2 版本开始,TiCDC 支持拆分单表事务功能,可大幅降低同步大事务的延时和内存消耗。因此,在业务对事务原子性要求不高的场景下,建议通过设置 sink uri 参数 transaction-atomicity
打开拆分事务功能以解决可能出现的同步延迟和 OOM 问题。
如果实际同步过程中仍然遇到了上述错误,建议将包含大事务部分的增量数据通过 BR 进行增量恢复,具体操作如下:
- 记录因为大事务而终止的 changefeed 的
checkpoint-ts
,将这个 TSO 作为 BR 增量备份的--lastbackupts
,并执行增量备份。 - 增量备份结束后,可以在 BR 日志输出中找到类似
["Full backup Failed summary : total backup ranges: 0, total success: 0, total failed: 0"] [BackupTS=421758868510212097]
的日志,记录其中的BackupTS
。 - 执行增量恢复。
- 建立一个新的 changefeed,从
BackupTS
开始同步任务。 - 删除旧的 changefeed。
有损 DDL 是指在 TiDB 中执行可能会导致数据改变的 DDL。一些常见的有损 DDL 操作包括:
- 修改列的类型,例如:INT -> VARCHAR
- 修改列的长度,例如:VARCHAR(20) -> VARCHAR(10)
- 修改列的精度,例如:DECIMAL(10, 3) -> DECIMAL(10, 2)
- 修改列的符号(有符号数/无符号数),例如:INT UNSIGNED -> INT SIGNED
在 TiDB v7.1.0 之前,TiCDC 会将一条新旧数据相同的 DML 事件同步到下游。当下游是 MySQL 时,这些 DML 事件不会产生任何数据变更,只有下游接收并执行该 DDL 语句后,数据才会发生变更。但是当下游是 Kafka 或者云存储时,TiCDC 会写入一条无用的数据到下游。
从 TiDB v7.1.0 开始,TiCDC 会过滤掉这些无用的 DML 事件,不再将它们同步到下游。
比如上游 TiDB 的建表语句为 create table test (id int primary key, ts timestamp)
,TiCDC 同步该语句到下游 MySQL 5.7,MySQL 使用默认配置,同步得到的表结构如下所示,timestamp 字段默认值会变成 CURRENT_TIMESTAMP
:
mysql root@127.0.0.1:test> show create table test;
+-------+----------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------+
| test | CREATE TABLE `test` ( |
| | `id` int(11) NOT NULL, |
| | `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | PRIMARY KEY (`id`) |
| | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+----------------------------------------------------------------------------------+
1 row in set
产生表结构不一致的原因是 explicit_defaults_for_timestamp
的默认值在 TiDB 和 MySQL 5.7 不同。从 TiCDC v5.0.1/v4.0.13 版本开始,同步到 MySQL 会自动设置 session 变量 explicit_defaults_for_timestamp = ON
,保证同步时间类型时上下游行为一致。对于 v5.0.1/v4.0.13 以前的版本,同步时间类型时需要注意 explicit_defaults_for_timestamp
默认值不同带来的兼容性问题。
TiCDC 提供至少一次的数据同步保证,当下游有重复数据时,会引起写冲突。为了避免该问题,TiCDC 会将 INSERT
和 UPDATE
语句转成 REPLACE INTO
语句。该行为由 safe-mode
参数来控制。
在 v6.1.3 版本之前,safe-mode
默认为 true
,即所有的 INSERT
和 UPDATE
语句都转成 REPLACE INTO
语句。在 v6.1.3 及之后版本,系统能自动判断下游是否存在重复数据,safe-mode
默认更改为 false
,当系统判断下游无重复数据时,会直接同步 INSERT
和 UPDATE
语句。
TiCDC 需要磁盘是为了缓冲上游写入高峰时下游消费不及时堆积的数据。TiCDC 正常运行期间都需要写入磁盘,但这通常不是同步吞吐和同步延时的瓶颈,写磁盘对延时影响在百毫秒内。TiCDC 也利用了内存来提升加速读取磁盘中的数据,以提升同步性能。
目前 TiCDC 尚未完全适配 TiDB Lightning 物理导入模式 (Physical Import Mode) 和 BR,请避免在使用 TiCDC 同步的表上使用 TiDB Lightning 物理导入模式和 BR。否则,可能会出现未知的错误,例如 TiCDC 同步卡住、同步延迟大幅增加、或者同步数据丢失。
如果有某些使用 TiCDC 同步的表需要使用 TiDB Lightning 物理导入模式或者 BR 恢复数据,可以这么做:
-
删除涉及这些表的 TiCDC 同步任务。
-
使用 TiDB Lightning 物理导入模式或 BR 在 TiCDC 的上游集群和下游集群分别恢复数据。
-
恢复完成并检查了上下游集群对应表的数据一致性之后,使用上游备份的时间点 (TSO) 作为 TiCDC 同步任务的
start-ts
,创建新的 TiCDC 同步任务,进行增量同步。例如,假设上游集群的 BR 备份的 snapshot 时间点为431434047157698561
,那么可以使用以下命令创建新的 TiCDC 同步任务:cdc cli changefeed create -c "upstream-to-downstream-some-tables" --start-ts=431434047157698561 --sink-uri="mysql://[email protected]:4000? time-zone="
当 changefeed 启动时,为了补齐 changefeed 暂停期间产生的增量数据日志,TiCDC 需要扫描 TiKV 中数据的历史版本,待扫描完毕后,才能够继续推进复制过程,扫描过程可能长达数分钟到数十分钟。
对于 v6.5.2 之前的版本,建议将 TiCDC 部署在下游 TiDB 集群。这是因为,如果上下游网络延迟较大,例如超过 100 ms 时,由于 MySQL 传输协议的原因,TiCDC 向下游执行 SQL 的延迟会急剧增加,导致系统的吞吐下降。部署在下游能够极大缓解该问题。经过优化后,v6.5.2 及之后的版本建议将 TiCDC 部署在上游集群。
目前,TiCDC 采用了以下执行顺序:
- TiCDC 阻塞受 DDL 影响的表的同步进度,直到 DDL
commitTS
的时间点,以确保在 DDLcommitTS
之前执行的 DML 先成功同步到下游。 - TiCDC 继续同步 DDL。当存在多个 DDL 时,TiCDC 是以串行的方式进行同步的。
- 当 DDL 在下游执行完成之后,TiCDC 继续同步 DDL
commitTS
之后执行的 DML。
如果下游是 TiDB 集群或者 MySQL,我们推荐使用 sync diff inspector 工具进行数据对比。
从 v7.1.0 起,TiCDC 支持 MQ sink 按照 TiKV Region 粒度来同步数据变更日志,实现处理能力上的可扩展性,使得 TiCDC 能够同步 Region 数量庞大的单表。如需开启,请在 TiCDC 配置文件中配置以下参数:
[scheduler]
enable-table-across-nodes = true
TiDB 有事务超时的机制,当事务运行超过 max-txn-ttl
后,会被 TiDB 强制回滚。TiCDC 遇到未提交的事务,会等待其提交后再继续同步其数据,因此会出现同步延迟。
因为通过 TiDB Operator 部署的 TiCDC 集群的默认端口号为 8301, 而 cdc cli 命令默认连接的 cdc 服务器的端口号是 8300。在使用 cdc cli 操作 TiCDC 集群时,你需要显式地指定 --server
参数,如下:
./cdc cli changefeed list --server "127.0.0.1:8301"
[
{
"id": "4k-table",
"namespace": "default",
"summary": {
"state": "stopped",
"tso": 441832628003799353,
"checkpoint": "2023-05-30 22:41:57.910",
"error": null
}
},
{
"id": "big-table",
"namespace": "default",
"summary": {
"state": "normal",
"tso": 441872834546892882,
"checkpoint": "2023-06-01 17:18:13.700",
"error": null
}
}
]