Try   HackMD

TiCDC 4.0 内部默认使用 old value 测试说明

注意: 这是一个易用性的改善,并不是一个 feature

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
落地过程

1. 需求来源

首先是来自 Jira 的 ticket。但是当我们做完之后发现其实很早就有一个 GitHub Issue 提了这个需求。

在该 ticket 中描述了用户使用上的问题:

  • 从技术上来说,开了 new collation 就必须要开 old value 才能正确同步
  • 这个限制是靠用户手动保证的,这种保证极其不可靠

并且还提出了对应需要改善的目标:

  • 不让用户操心什么是 new collation,和 old value 有什么关系,如果检查到某张表开启 new collation,就自动以开了 old value 的模式去拉 tikv 数据
  • 如果用户在配置上没有开启old value,那么希望 cdc 对下游的输出也是没有 old value 的

注意:如果一个表使用了 new collation,那么它对应的 index 编排方式会发生改变,导致无法通过变更后的 new value 所在 key 反向解析到原数据是怎么样的,导致 TiCDC 无法还原对应的 SQL,最终导致同步失败,所以如果开启了 new collation 那么就需要开启 old value 来拉去 TiKV 数据。

2. 需求演变

在需求的讨论过程中,我们发现在 TiCDC 内部中去判断某张表是否开启 new collation 然后再对应使用开启 old value 的模式去拉数据这个机制非常的复杂。所以讨论决定无论开不开启 new collation 我们都需要使用开启 old value 的模式去拉数据。

对于第二个目标没有调整,在用户没有开启 old value 配置时,我们需要保证输出不发生变化。

最终将需求确认好后,创建了对应的 GitHub Issue

3. 提交 PR 落地

提交 PR 来支持内部总是使用开启 old value 模式来拉 TiKV 的数据。实现该功能过程中,遇到的最核心的问题就是:用户在没有开启 old value 配置时,我们需要保持输出数据的格式不发生变化

首先我们需要明确在开启 old value 和不开启 old value 模式时原来的 TiCDC 的行为:

开启 old value 不开启 old value
更新数据 输出旧的值和新的值 只输出新的值,但是如果修改的是 handle key 的列,那么我们需要将更新事件拆分成一个删除事件和一个插入事件
删除数据 输出被删除的行数据中的所有列 只输出被删除数据的 handle key 的列

具体的可以参考 TiCDC Open Protocol 的说明。注意:该说明中对不开启 old value 的更新事件的说明不够仔细。

4. 测试 PR

首先明确我当时做测试主要测试的两个目标:

  1. 在不开启 old vlaue 配置时输出和修改之前行为一致
  2. 在数据库开启 new collation 之后,在不开启 old value 时也能正确同步数据

所以我在 PR 上做了如下测试:

测试用到的 sql 如下:

CREATE DATABASE `tidb_test` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;;
USE `tidb_test`;
CREATE TABLE t(
  id varchar(20) charset utf8mb4 collate utf8mb4_general_ci, 
  name varchar(20) charset utf8mb4 collate utf8mb4_general_ci,
  age int,
  primary key(id, name));
INSERT INTO t VALUES ('AA', 'he', 10);
INSERT INTO t VALUES ('BB', 'le', 20);
INSERT INTO t VALUES ('CC', 'me', 30);
DELETE FROM t WHERE `id` = 'AA';
UPDATE t SET id='FF' WHERE age=30;
UPDATE t SET age=55 WHERE id='BB';

测试的用例:

  • 不开启 new collation

    • 开启 old value 配置,对比插入、删除、更新的输入是否与旧的 TiCDC 对齐。
    • 不开启 old vlaue 配置,对比插入、删除、更新的输入是否与旧的 TiCDC 对齐。
  • 开启 new collation

    • 开启 old value 配置,对比插入、删除、更新的输入是否与旧的 TiCDC 对齐。
    • 不开启 old vlaue 配置,对比插入、删除、更新的输入是否与旧的 TiCDC 对齐(旧的 TiCDC 不能正常同步数据,新的可以)。

根据以上的用例,我进行了以下的测试:

不开启 new collation

开启 new collation

我的基本测试步骤:

  1. 根据配置启动集群
  2. 启动 cdc server (把日志级别修改到 debug)
  3. 创建changefeed ./cdc cli changefeed create --sink-uri="blackhole://" --changefeed-id="test"
  4. mysql host 127.0.0.1 port 4000 -u root -p comments < tidb_test.sql
  5. grep "BlockHoleSink: EmitRowChangedEvents" ticdc.log 查看数据输出并进行比对。

4.0 的 PR:https://github.com/pingcap/ticdc/pull/2322
5.0 的 PR:https://github.com/pingcap/ticdc/pull/2323
5.1 的 PR:https://github.com/pingcap/ticdc/pull/2324

注意:TiCDC 从 5.0 以后默认开启 old value。