记录一次DMS无锁变更的坑

记录一次DMS无锁变更的坑
逐暗者背景
在阿里云DMS上执行了一个DDL语句,本意是将value1和value2增加联合唯一索引,并将value1的字段从可空表更为不可空。
1 | ALTER TABLE TABLE ADD CONSTRAINT uk_value1_value2 UNIQUE KEY (value1,value2); |
执行之后导致原表丢失上千条数据,突然比较好奇,为啥ALTER语句会造成数据删除呢?
复现
初始准备
1 | CREATE TABLE test ( |
再初始化几条测试数据
1 |
|
使用sql脚本进行变更
1 | ALTER TABLE test ADD CONSTRAINT uk_value1_value2 UNIQUE KEY (value1,value2); |
可以发现第一个ALTER语句顺利执行,联合唯一索引中的一个字段值为NULL,其余值相同不会被认为是重复数据。
第二个ALTER语句执行失败,因为设置了NOT NULL,但是没有设置DEFAULT 值,原数据中存在NULL值,所以执行失败了。
使用DMS工单来执行
这里采用sql语句来模拟DMS工单执行
1 | CREATE TABLE tmp_table_name LIKE test; |
发现sql顺利执行完成,但表中的数据丢失了2条。
通过SQL客户端工具查看信息,能得到一些告警
原因是唯一索引冲突了,并且被忽略了。
原因
DMS执行逻辑
DMS是先创建一个临时表,再将ALTER语句执行到临时表上,接着使用INSERT IGNORE INTO 复制原表数据。
再接着使用Binlog增量同步到新表上,最后rename一下表名,替换原表。
这和我们直接修改执行ALTER有些差异:
- 由于是空表,ALTER语句并不会报错(例如非空、唯一索引约束等等)
- 由于使用了INSERT IGNORE INTO ,导致null值被错误的赋值了默认值(表结构中没有default值定义),导致唯一索引冲突,并忽略了错误。
疑问
- 按照这个流程,如果原表存在重复数据,也能被加上唯一索引,只是会丢数据。事实却相反,DMS会报错。
参考DMS无锁变更说明,发现唯一索引被用于了全量表分段操作和增量更新,这可能是执行新增唯一索引会报错的原因。
目标表中有主键或唯一键:在执行无锁结构变更时,主键或唯一键用于全量拷表分段操作及后续增量更新。
说明
如果目标表中仅有主键或唯一键,在执行结构变更期间更新主键或唯一键,将导致任务执行失败。
- 为什么INSERT IGNORE INTO会把NULL值复制到NOT NULL字段上时默认设置一个未指定的默认值?
Data conversions that would trigger errors abort the statement if IGNORE is not specified. With IGNORE, invalid values are adjusted to the closest values and inserted; warnings are produced but the statement does not abort. You can determine with the mysql_info() C API function how many rows were actually inserted into the table.
如果未指定IGNORE,会触发错误的数据转换将终止语句。使用IGNORE时,无效值会被调整为最接近的值并插入;会产生警告,但语句不会终止。你可以使用mysql_info() C API 函数来确定实际插入到表中的行数。