gh-ost唯一索引变更可能导致表数据丢失
2017-08-30 18:38 Kevin.hhl 阅读(772) 评论(0) 收藏 举报一.问题起因
下午在给测试库加unique key发现gh-ost无法退出,即使我把postpone文件删掉,gh-ost还是无法正常退出,于是想仔细回溯gh-ost加unique key的整个流程,思考gh-ost和正常DDL的区别,然后在测试环境跑case,整个流程在下面会介绍。
二.案例及分析
2.1 测试case
因线上数据敏感性,故用测试环境来复现:
--测试表原结果 CREATE TABLE `t1` ( `id` int(11) NOT NULL, `c1` varchar(4) DEFAULT NULL, `c2` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c1` (`c1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; mysql> insert into t1 values(4,'ccc',3); Query OK, 1 row affected (0.01 sec) mysql> select * from t1; +----+------+------+ | id | c1 | c2 | +----+------+------+ | 1 | aaa | 1 | | 2 | bbb | 2 | | 3 | ccc | 3 | | 4 | ccc | 3 | +----+------+------+ 4 rows in set (0.00 sec) 到此为t1表增加unique key uniq_c1c2(c1,c2),此时t1表有2条重复数据 然后执行gh-ost,脚本大致命令如下: alter="add unique key uniq_c1c2(c1,c2);" log="/tmp/${pid}.gh-ost_${h}_${p}_${d}_${t}.log"; postpone="/tmp/${pid}.gh-ost_${h}_${p}_${d}_${t}.postpone"; panic="/tmp/i${pid}.gh-ost_${h}_${p}_${d}_${t}.panic"; /ghost/gh-ost \ --host=${h} \ --port=${p} \ --conf=gh.cnf \ --database="${d}" \ --table="${t}" \ --alter="${alter}" \ --verbose \ --initially-drop-ghost-table \ --assume-rbr \ --max-lag-millis=1000 \ --cut-over=default \ --exact-rowcount \ --concurrent-rowcount \ --default-retries=120 \ --initially-drop-old-table \ --panic-flag-file=${panic} --execute >${log} 2>&1 执行gh-ost 发现t1 少了一条重复数据,如下: mysql> select * from t1; +----+------+------+ | id | c1 | c2 | +----+------+------+ | 1 | aaa | 1 | | 2 | bbb | 2 | | 3 | ccc | 3 | +----+------+------+ 3 rows in set (0.00 sec)
2.2 原因分析
在master抓包分析发现insert到gho(临时中间表)是insert ignore .....
insert /* gh-ost `ccut`.`t1` */ ignore into `ccut`.`_t1_gho` (`id`, `c1`, `c2`) (select `id`, `c1`, `c2` from `ccut`.`t1` force index (`PRIMARY`) where (((`id` > '1') or ((`id` = '1'))) and ((`id` < 4) or ((`id` = 4)))) lock in share mode );
验证了重复数据被ignore掉。
翻看gh-ost 代码:
-- go/sql/builder.go的第218-229行代码如下: if transactionalTable { transactionalClause = "lock in share mode" } result = fmt.Sprintf(` insert /* gh-ost %s.%s */ ignore into %s.%s (%s) (select %s from %s.%s force index (%s) where (%s and %s) %s ) `, databaseName, originalTableName, databaseName, ghostTableName, mappedSharedColumnsListing, sharedColumnsListing, databaseName, originalTableName, uniqueKey, rangeStartComparison, rangeEndComparison, transactionalClause) return result, explodedArgs, nil
可以看到gh-ost确实用insert ignore处理。
三.总结
给已有数据的表加unique key时,如果使用gh-ost,数据可能面临丢失。重复数据是否删除需要和业务方确认。
新工具虽好,一些细节需要大量的测试再评估,避开可能存在的问题,例如加unique key使用其他方式来替代等。
提Bug给gh-ost社区,希望可以推进gh-ost修复此问题。
浙公网安备 33010602011771号