Skip to content

Commit

Permalink
Update ticdc-split-update-behavior.md
Browse files Browse the repository at this point in the history
  • Loading branch information
hfxsd committed Jul 18, 2024
1 parent 162a76f commit 2c64224
Showing 1 changed file with 3 additions and 79 deletions.
82 changes: 3 additions & 79 deletions ticdc/ticdc-split-update-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,7 @@ aliases: ['/zh/tidb/dev/ticdc-behavior-change']

# TiCDC 拆分 UPDATE 事件行为说明

<<<<<<< HEAD:ticdc/ticdc-behavior-change.md
## `UPDATE` 事件拆分为 `DELETE``INSERT` 事件

### 含有单条 `UPDATE` 变更的事务拆分

从 v6.5.3 开始,使用非 MySQL Sink 时,对于仅包含一条 `UPDATE` 变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该条事件拆分为 `DELETE``INSERT` 两条事件。详情见 GitHub issue [#9086](https://github.com/pingcap/tiflow/issues/9086)

该变更主要为了解决如下问题:

* 在使用 CSV 和 AVRO 协议时,仅输出新值而不输出旧值。因此,当主键或者非空唯一索引的列值发生改变时,消费者只能接收到变化后的新值,无法得到旧值,导致无法处理变更前的值(例如删除旧值)。
* 在使用 Index value dispatcher 将数据按照 Key 分发到不同的 Kafka partition 时,下游的消费者组内多个消费者进程独立消费 Kafka Topic partition,由于消费进度不同,可能导致数据不一致的问题。

以如下 SQL 为例:

```sql
CREATE TABLE t (a INT PRIMARY KEY, b INT);
INSERT INTO t VALUES (1, 1);
UPDATE t SET a = 2 WHERE a = 1;
```

在上述示例中,主键 `a` 的值从 `1` 修改为 `2`。如果不将该 `UPDATE` 事件进行拆分:

* 在使用 CSV 和 AVRO 协议时,消费者仅能看到新值 `a = 2`,而无法得到旧值 `a = 1`。这可能导致下游消费者只插入了新值 `2`,而没有删除旧值 `1`
* 在使用 Index value dispatcher 时,插入记录 `(1, 1)` 的事件可能被发送到 Partition 0,而变更事件 `(2, 1)` 可能被发送到 Partition 1。如果 Partition 1 的消费进度快于 Partition 0,则可能由于下游数据系统中找不到相应数据而导致出错。因此,TiCDC 会将该条 `UPDATE` 事件拆分为 `DELETE``INSERT` 两条事件,其中,删除记录 `(1, 1)` 被发送到 Partition 0,写入记录 `(2, 1)` 被发送到 Partition 1,以确保无论消费者的进度如何,事件都能被消费成功。

### 含有多条 `UPDATE` 变更的事务拆分

从 v6.5.4 开始,对于一个含有多条变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该其拆分为 `DELETE``INSERT` 两条事件,并确保所有事件按照 `DELETE` 事件在 `INSERT` 事件之前的顺序进行排序。详情见 GitHub issue [#9430](https://github.com/pingcap/tiflow/issues/9430)

该变更主要为了解决当使用 MySQL Sink 直接将这两条事件写入下游时,可能会出现主键或唯一键冲突的问题,从而导致 changefeed 报错。当使用 Kafka Sink 或其他 Sink 时,如果消费者需要将数据变更写入关系型数据库或进行类似操作,也可能遇到相同问题。

以如下 SQL 为例:

```sql
CREATE TABLE t (a INT PRIMARY KEY, b INT);
INSERT INTO t VALUES (1, 1);
INSERT INTO t VALUES (2, 2);

BEGIN;
UPDATE t SET a = 3 WHERE a = 1;
UPDATE t SET a = 1 WHERE a = 2;
UPDATE t SET a = 2 WHERE a = 3;
COMMIT;
```

在上述示例中,通过执行三条 SQL 语句对两行数据的主键进行交换,但 TiCDC 只会接收到两条 `UPDATE` 变更事件,即将主键 `a``1` 变更为 `2`,将主键 `a``2` 变更为 `1`,如果 MYSQL Sink 直接将这两条 `UPDATE` 事件写入下游,会出现主键冲突的问题,导致 changefeed 报错。

因此,TiCDC 会将这两条事件拆分为四条事件,即删除记录 `(1, 1)``(2, 2)` 以及写入记录 `(2, 1)``(1, 2)`

### MySQL Sink
=======
## MySQL Sink 拆分 `UPDATE` 事件行为说明
>>>>>>> b01d47ca4e (ticdc: add output-raw-change-event parameter and update ticdc-behavior-change (#17699)):ticdc/ticdc-split-update-behavior.md

从 v6.5.10 开始,当使用 MySQL Sink 时,TiCDC 的任意节点在收到某张表的同步任务请求并开始向下游同步数据之前,会从 PD 获取当前的时间戳 `thresholdTS`,并根据时间戳的值决定是否拆分 `UPDATE` 事件:

Expand Down Expand Up @@ -125,7 +73,7 @@ UPDATE t SET a = 3 WHERE a = 2;

### 含有单条 `UPDATE` 变更的事务拆分

v6.5.3v7.1.1v7.2.0 开始,使用非 MySQL Sink 时,对于仅包含一条 `UPDATE` 变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该条事件拆分为 `DELETE``INSERT` 两条事件。详情见 GitHub issue [#9086](https://github.com/pingcap/tiflow/issues/9086)。
v6.5.3 开始,使用非 MySQL Sink 时,对于仅包含一条 `UPDATE` 变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该条事件拆分为 `DELETE``INSERT` 两条事件。详情见 GitHub issue [#9086](https://github.com/pingcap/tiflow/issues/9086)。

该变更主要为了解决在使用 CSV 和 AVRO 协议时,TiCDC 在默认配置下仅输出新值而不输出旧值的问题。因此,当主键或者非空唯一索引的列值发生改变时,消费者只能接收到变化后的新值,无法得到旧值,导致无法处理变更前的值(例如删除旧值)。以如下 SQL 为例:

Expand All @@ -139,7 +87,7 @@ UPDATE t SET a = 2 WHERE a = 1;

### 含有多条 `UPDATE` 变更的事务拆分

v6.5.4v7.1.2v7.4.0 开始,对于一个含有多条变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该其拆分为 `DELETE``INSERT` 两条事件,并确保所有事件按照 `DELETE` 事件在 `INSERT` 事件之前的顺序进行排序。详情见 GitHub issue [#9430](https://github.com/pingcap/tiflow/issues/9430)。
v6.5.4 开始,对于一个含有多条变更的事务,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该其拆分为 `DELETE``INSERT` 两条事件,并确保所有事件按照 `DELETE` 事件在 `INSERT` 事件之前的顺序进行排序。详情见 GitHub issue [#9430](https://github.com/pingcap/tiflow/issues/9430)。

该变更主要为了解决当使用 Kafka Sink 或其他 Sink 时,由于 TiCDC 接收到的 `UPDATE` 事件顺序可能不正确,消费者将数据变更写入关系型数据库或进行类似操作,可能遇到主键或唯一键冲突的问题。

Expand All @@ -163,7 +111,7 @@ COMMIT;

### 控制是否拆分主键或唯一键 `UPDATE` 事件

v6.5.10v7.1.6v7.5.3v8.1.1 开始,使用非 MySQL Sink 时,TiCDC 支持通过 `output-raw-change-event` 参数控制是否拆分主键或唯一键 `UPDATE` 事件,详情见 GitHub issue [#11211](https://github.com/pingcap/tiflow/issues/11211)。这个参数的具体行为是:
v6.5.10 开始,使用非 MySQL Sink 时,TiCDC 支持通过 `output-raw-change-event` 参数控制是否拆分主键或唯一键 `UPDATE` 事件,详情见 GitHub issue [#11211](https://github.com/pingcap/tiflow/issues/11211)。这个参数的具体行为是:

-`output-raw-change-event = false` 时,如果 `UPDATE` 事件的主键或者非空唯一索引的列值发生改变,TiCDC 会将该其拆分为 `DELETE``INSERT` 两条事件,并确保所有事件按照 `DELETE` 事件在 `INSERT` 事件之前的顺序进行排序。
-`output-raw-change-event = true` 时,TiCDC 不拆分 `UPDATE` 事件。注意,当表的主键为聚簇索引时,对主键的更新会在 TiDB 中拆分为 `DELETE``INSERT` 两个事件,该行为不受 `output-raw-change-event` 参数的影响。
Expand All @@ -178,27 +126,3 @@ COMMIT;
| v6.5.4 | Canal/Open | ✗ | ✗ | 只拆分并排序包含多条变更的事务 |
| v6.5.5v6.5.9 | 所有协议 | ✓ | ✗ | |
| \>= v6.5.10 | 所有协议 | ✓ (默认值:`output-raw-change-event = false`) | ✓ (可选配置项:`output-raw-change-event = true`) | |

#### Release 7.1 的兼容性

| 版本 | 协议 | 拆分主键或唯一键 `UPDATE` 事件 | 不拆分主键或唯一键 `UPDATE` 事件 | 备注 |
| -- | -- | -- | -- | -- |
| v7.1.0 | 所有协议 | ✗ | ✓ |  |
| v7.1.1 | Canal/Open | ✗ | ✓ |  |
| v7.1.1 | CSV/Avro | ✗ | ✗ | 拆分但是不排序, 详见 [#9086](https://github.com/pingcap/tiflow/issues/9658) |
| v7.1.2 ~ v7.1.5 | 所有协议 | ✓ | ✗ |  |
| \>= v7.1.6(待发布)| 所有协议 | ✓ (默认值:`output-raw-change-event = false`) | ✓ (可选配置项:`output-raw-change-event = true`) | |

#### Release 7.5 的兼容性

| 版本 | 协议 | 拆分主键或唯一键 `UPDATE` 事件 | 不拆分主键或唯一键 `UPDATE` 事件 | 备注 |
| -- | -- | -- | -- | -- |
| <= v7.5.2 | 所有协议 | ✓ | ✗ | |
| \>= v7.5.3(待发布) | 所有协议 | ✓ (默认值:`output-raw-change-event = false`) | ✓ (可选配置项:`output-raw-change-event = true`) | |

#### Release 8.1 的兼容性

| 版本 | 协议 | 拆分主键或唯一键 `UPDATE` 事件 | 不拆分主键或唯一键 `UPDATE` 事件 | 备注 |
| -- | -- | -- | -- | -- |
| v8.1.0 | 所有协议 | ✓ | ✗ | |
| \>= v8.1.1(待发布) | 所有协议 | ✓ (默认值:`output-raw-change-event = false`) | ✓ (可选配置项:`output-raw-change-event = true`) | |

0 comments on commit 2c64224

Please sign in to comment.