上游TiDB存在单条语句对多表进行RENAME时,通过TiCDC同步到下游时会写错表名

最近在通过TiCDC搭建TiDB备份集群时,发现有个坑,但是官方文档里没有写。具体内容是搭建好同步关系后,在校验数据时,发现几个预估表的数据同步有问题,数据同步到下游表名发生了变化。经排查是TiCDC的 BUG:

  • TiCDC在拉取KV change log时,维护了table_id -> table_name的映射关系,例如{‘1’: ‘a’, ‘2’: ‘new_a’}
  • 由于TiCDC不支持原子化一次性重命名多个表名,当解析到rename table a to bak_a, new_a to a;这种语句时,会直接打印日志然后返回,不会修改table_id -> table_name的映射关系,也就是虽然上游table_id=2的表已经被重命名为a,但是在TiCDC映射关系里,table_name还是new_a
  • 在这种情况下,上游TiDB上是写入的是重命名后的a表名,而数据同步到下游时,拼接的SQL中是new_a表。

目前的解决方案是: 让修改清空表方式,直接改成TRUNCATE操作。但是这种操作其实很不优雅,本来是对需要清空的表,做一下备份,如果业务出现问题还可以通过rename表的方式很快回滚,现在如果业务出现问题,就没办法处理了。

另外这种BUG,仅仅是因为TiCDC只支持特定的DDL,对不支持的DDL操作会有未知风险。这种风险我是可以理解的,但是坑的是,在官方文档里并没有提及支持的DDL列表,只能在代码里翻到,也就是只能踩坑后才能发现。类似的还有:建议上游同步的表不超过2K(这个也是在asktug的一个帖子里看到官方回复才知道),每个参数的真正含义是什么?当出现同步效率低下时,应该如何调优?这些官方文档都没有提及。

希望官方把TiCDC的文档维护的更好,把TiCDC的使用限制、参数说明、调优指南更丰富一下,这样对用户在选型、方案设计方面会很有帮助,在这方面TiKV、TiDB Server文档写的就很好。

1赞

BTW,支持的DDL列表为:

func (f *Filter) shouldDiscardByBuiltInDDLAllowlist(ddlType model.ActionType) bool {
	/* The following DDL will be filter:
	ActionAddForeignKey                 ActionType = 9
	ActionDropForeignKey                ActionType = 10
	ActionRebaseAutoID                  ActionType = 13
	ActionShardRowID                    ActionType = 16
	ActionLockTable                     ActionType = 27
	ActionUnlockTable                   ActionType = 28
	ActionRepairTable                   ActionType = 29
	ActionSetTiFlashReplica             ActionType = 30
	ActionUpdateTiFlashReplicaStatus    ActionType = 31
	ActionCreateSequence                ActionType = 34
	ActionAlterSequence                 ActionType = 35
	ActionDropSequence                  ActionType = 36
	ActionModifyTableAutoIdCache        ActionType = 39
	ActionRebaseAutoRandomBase          ActionType = 40
	ActionAlterIndexVisibility          ActionType = 41
	ActionExchangeTablePartition        ActionType = 42
	ActionAddCheckConstraint            ActionType = 43
	ActionDropCheckConstraint           ActionType = 44
	ActionAlterCheckConstraint          ActionType = 45
	ActionAlterTableAlterPartition      ActionType = 46

	... Any Action which of value is greater than 46 ...
	*/
	switch ddlType {
	case model.ActionCreateSchema,
		model.ActionDropSchema,
		model.ActionCreateTable,
		model.ActionDropTable,
		model.ActionAddColumn,
		model.ActionDropColumn,
		model.ActionAddIndex,
		model.ActionDropIndex,
		model.ActionTruncateTable,
		model.ActionModifyColumn,
		model.ActionRenameTable,
		model.ActionSetDefaultValue,
		model.ActionModifyTableComment,
		model.ActionRenameIndex,
		model.ActionAddTablePartition,
		model.ActionDropTablePartition,
		model.ActionCreateView,
		model.ActionModifyTableCharsetAndCollate,
		model.ActionTruncateTablePartition,
		model.ActionDropView,
		model.ActionRecoverTable,
		model.ActionModifySchemaCharsetAndCollate,
		model.ActionAddPrimaryKey,
		model.ActionDropPrimaryKey,
		model.ActionAddColumns,
		model.ActionDropColumns:
		return false
	}
	return true
}

:+1::+1::+1: 已反馈

1赞

另外这种情况下,由于rename table a to bak_a, new_a to a;这条语句直接被ignore了,所以下游TiDB上a表其实还是有数据的,这时候如果有业务查询, 获取就是脏数据了,也是需要注意下。

其实我建议,后续这种非用户预期的行为,宁可直接报错,让changefeed暂停,也别直接ignore,造成的后果其实更大

不过,官方对论坛响应确实很快,有这个态度TiDB肯定会越来越好的:grinning:

TiDC这块感觉文档还是差一点,比如tikv侧的cdc的原理 架构,cdc本身的内部架构 线程等还是有点少,像DM的就比较详细些,也有一些源码解析文档

嗯啊,cdc出现问题,搜索出来的文档就特别少,只能自己摸索了