TiCDC 表路由 从 v8.5.7 开始引入
TiCDC 表路由 (Table Routing) 允许你通过 changefeed 配置,将上游表映射到指定的下游库名或表名。该功能仅适用于 TiCDC 新架构,不支持 TiCDC 老架构。
表路由只会改变 TiCDC 输出到下游的库名和表名,不会改变行数据、列名、表结构、表过滤规则、Topic 分发规则、Partition 分发规则或列选择规则。
使用场景
表路由适用于以下场景:
- 将
sales.orders同步到archive.sales_orders,或同步到符合其他下游命名规范的表。 - 将多个源库同步到同一个目标库,并通过保持目标表名唯一区分源库,例如将
tenant_001.orders同步到tenant_mirror.tenant_001_orders。 - 构建迁移、容灾、归档或影子 Changefeed,避免写入与上游同名的下游对象。
- 向 MQ 消费程序或存储服务暴露稳定的库表名。
注意
表路由仅支持上游表到下游目标表的一对一映射,不支持将多张上游表合并到同一张下游表。 表路由不支持将一张上游表拆分到多张下游表,或转换行数据内容。
配置表路由
配置前,请先启用 TiCDC 新架构。详情参见 newarch。
以下示例将 sales.orders 路由到 archive.sales_orders:
[sink]
[[sink.dispatchers]]
matcher = ["sales.orders"]
target-schema = "archive"
target-table = "{schema}_{table}"Changefeed 启动后,sales.orders 的 DML 和 DDL 事件会写入下游的 archive.sales_orders。
注意
同一个上游数据库中的不同表可以路由到不同的目标库,但此类配置只适用于 DML 和表级 DDL。
- 对于
CREATE DATABASE、DROP DATABASE和ALTER DATABASE这类库级 DDL,TiCDC 必须能根据路由规则确定唯一的目标库,否则该 DDL 同步会失败。- 如果要将同一个上游数据库中的不同表路由到不同的目标库,你需要提前创建下游目标库,并避免在上游执行需要自动同步的库级 DDL。
配置字段
你可以通过 sink.dispatchers 的以下字段配置表路由功能。
| 字段 | 描述 |
|---|---|
matcher | 匹配上游库表。语法与表库过滤语法相同,包括 sales.* 这类通配符匹配,以及 !sales.tmp_* 这类排除匹配。 |
target-schema | 指定下游库名。如果不设置该字段,TiCDC 保持上游库名不变。 |
target-table | 指定下游表名。如果不设置该字段,TiCDC 保持上游表名不变。 |
匹配行为如下:
- 只有设置了
target-schema或target-table的sink.dispatchers规则才会参与表路由。 - 如果一张表匹配多条表路由规则,只有
sink.dispatchers中第一条匹配的规则会生效。 matcher始终匹配上游库表名,而不是路由后的目标库表名。- Changefeed 配置项
case-sensitive只影响表路由的matcher是否大小写敏感,不会改写{schema}和{table}展开结果的大小写。详情参见case-sensitive。
占位符
你可以在 target-schema 和 target-table 中使用以下占位符:
| 占位符 | 描述 |
|---|---|
{schema} | 上游库名,保留实际匹配到的库名大小写。 |
{table} | 上游表名,保留实际匹配到的表名大小写。 |
target-schema 和 target-table 的值只能包含字面文本、{schema} 和 {table}。如果使用 {db} 这类未知占位符,TiCDC 会拒绝该 Changefeed 配置,并返回 CDC:ErrInvalidTableRoutingRule 错误。
以源表 sales.orders 为例:
| 配置 | 目标表 |
|---|---|
target-schema = "archive" | archive.orders |
target-table = "{table}_bak" | sales.orders_bak |
target-schema = "{schema}_mirror" | sales_mirror.orders |
target-schema = "archive" 和 target-table = "{schema}_{table}" | archive.sales_orders |
示例
路由一个库中的所有表
以下配置将 sales 库中的所有表路由到 archive 库,并在目标表名后追加 _bak:
[filter]
rules = ["sales.*"]
[sink]
[[sink.dispatchers]]
matcher = ["sales.*"]
target-schema = "archive"
target-table = "{table}_bak"路由结果示例如下:
sales.orders会路由到archive.orders_bak。sales.order_items会路由到archive.order_items_bak。
将多个库路由到同一个目标库
将多个库路由到同一个目标库时,建议在 target-table 中包含 {schema},以确保目标表名唯一。
[filter]
rules = ["sales.*", "crm.*", "finance.*"]
[sink]
[[sink.dispatchers]]
matcher = ["sales.*", "crm.*", "finance.*"]
target-schema = "archive"
target-table = "{schema}_{table}"路由结果示例如下:
sales.orders会路由到archive.sales_orders。crm.orders会路由到archive.crm_orders。finance.orders会路由到archive.finance_orders。
注意
该配置仅适用于合库,不适用于合表。表路由不支持将多个不同库中的同名表合并到同一张下游表。
同时使用表路由和 Kafka Sink Topic、Partition 分发器
同一条 dispatcher 规则可以同时包含表路由字段和已有的分发字段:
[filter]
rules = ["sales.orders"]
[sink]
[[sink.dispatchers]]
matcher = ["sales.orders"]
topic = "order-events"
partition = "index-value"
target-schema = "public"
target-table = "orders"在以上示例中,表路由将下游数据中暴露的库表名改为 public.orders。
表路由不会改变 Topic 或 Partition 的分发结果,topic 和 partition 分发器仍然按照上游表 sales.orders 进行匹配和分发计算。
输出行为
| Sink | 行为 |
|---|---|
| MySQL Sink | DDL 和 DML 语句会写入路由后的目标库表。开启 Redo 功能时,执行 redo apply 会将事件回放到路由后的目标表。 |
| Kafka Sink 和 Pulsar Sink | 协议中的 payload 字段值和 DDL 事件中的 query 字段值使用路由后的目标库表名;编码协议中的 schema、table 字段值也会使用路由后的目标库表名。 |
| Cloud Storage Sink | TiCDC 根据路由后的目标库表名生成对应的存储路径、schema 文件、表定义文件和数据文件。 |
DDL 行为
启用表路由后,TiCDC 会改写 DDL 语句,使结构化 DDL 元数据和 SQL 文本中的库表名保持一致。
例如,如果配置以下规则:
[[sink.dispatchers]]
matcher = ["sales.*"]
target-schema = "archive"
target-table = "{table}_routed"TiCDC 会将以下上游 DDL:
RENAME TABLE `sales`.`temp_table` TO `sales`.`renamed_table`;改写为以下下游 DDL:
RENAME TABLE `archive`.`temp_table_routed` TO `archive`.`renamed_table_routed`;如果 DDL 语句中包含表引用,且这些表引用匹配表路由规则,TiCDC 也会改写被引用的表名。例如,CREATE VIEW 语句中的表引用,以及 ALTER TABLE 语句中的外键引用都可以被改写。
对于 CREATE DATABASE、DROP DATABASE 和 ALTER DATABASE ... CHARACTER SET/COLLATE 这类库级 DDL,如果库名匹配表路由规则,TiCDC 会改写库名。如果同一个上游库匹配多条表路由规则,且这些规则解析出的目标库名不同,TiCDC 无法为该库级 DDL 确定唯一目标库,Changefeed 会返回表路由错误。
创建或更新 Changefeed 时,TiCDC 会基于当前同步范围内的表检查目标表冲突。运行期间,TiCDC 会在同步 CREATE TABLE、RENAME TABLE、DROP TABLE、DROP DATABASE 等 DDL 时更新冲突检测状态。库级 DDL 是否能确定唯一目标库,会在同步对应 DDL 时判断。
路由冲突检测
当两张不同的上游表在同一个 Changefeed 内被路由到同一个下游 (schema, table) 时,会发生路由冲突。TiCDC 不支持将多张上游表合并到同一张下游表。
例如,以下配置可能导致冲突:
[filter]
rules = ["sales.*", "crm.*"]
[sink]
[[sink.dispatchers]]
matcher = ["sales.*"]
target-schema = "archive"
target-table = "{table}"
[[sink.dispatchers]]
matcher = ["crm.*"]
target-schema = "archive"
target-table = "{table}"如果 sales.orders 和 crm.orders 都在同步范围内,这两张表都会被路由到 archive.orders。TiCDC 会拒绝创建或更新 Changefeed,并返回 CDC:ErrTableRouteConflict 错误。
Changefeed 运行期间,如果 CREATE TABLE 或 RENAME TABLE 等 DDL 导致两张在当前同步范围内的上游表被路由到同一张目标表,Changefeed 会失败并返回 CDC:ErrTableRouteConflict 错误。
如果上游表被删除或重命名,TiCDC 会解除该上游表与目标表之间的路由关系。之后,新的上游表可以继续使用该目标表,但在同一时刻同一个目标表只能对应一张正在被当前 Changefeed 同步的上游表。
警告:
路由冲突检测仅限于单个 Changefeed。如果多个 Changefeed 写入同一个下游系统,请确保这些 Changefeed 的表路由规则不会路由到相同的目标对象。
故障排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
创建或更新 Changefeed 时报 CDC:ErrInvalidTableRoutingRule 错误。 | matcher 语法无效,或 target-schema、target-table 包含未知占位符或不匹配的大括号。 | 检查 matcher 是否符合表库过滤语法,并确保 target-schema 和 target-table 只使用字面文本、{schema} 和 {table}。 |
| MQ Topic 名仍然使用上游库表名。 | 表路由不会改变 Topic 或 Partition 分发规则。 | 如果需要修改 Topic 名,请在 sink.dispatchers 中单独配置 topic。 |
DDL 同步时报 CDC:ErrTableRoutingFailed 错误。 | 该 DDL 无法安全地进行表路由改写,或库级 DDL 存在目标库歧义。 | 检查 DDL 类型和路由规则。对于库级 DDL,确保同一个上游库只能解析到一个目标库。 |
Changefeed 运行中失败并报 CDC:ErrTableRouteConflict 错误。 | 新建表或重命名表后,两个不同的上游表被路由到同一个下游表。 | 调整表路由规则或上游 DDL,确保单个 Changefeed 内每个目标表在同一时刻只对应一张正在被当前 Changefeed 同步的上游表。 |