背景
MySQL保存了两份元数据,一份在server层,保存在FRM文件中,另外一份在引擎层,比如InnoDB的数据字典中,这样也就造成了DDL语句经常导致元数据不一致的情况,下面介绍两个近期出现的因为DDL产生的bug。
rename 外键引用的column
BUG复现过程
CREATE TABLE t1 (a INT NOT NULL,
b INT NOT NULL,
INDEX idx(a)) ENGINE=InnoDB;
CREATE TABLE t2 (a INT KEY, b INT, INDEX ind(b),
FOREIGN KEY (b) REFERENCES t1(a)
ON DELETE CASCADE
ON UPDATE CASCADE) ENGINE=InnoDB;
ALTER TABLE t1 CHANGE a id INT;
在DEBUG版本下,MySQL实例是crash的,crash在 handler0alter.cc
line 5643。
BUG原因分析
在做alter语句的时候,因为rename的column关联有foreign key,所以需要在数据字典中更改这个foreign key,但在数据字典缓存中还保留了一份foreign对象,代码只做了持久化的更改,在重新dict_load_foreigns()
的时候,和缓存中的foreign key冲突,导致函数报错。
BUG修复方法
BUG修复也比较简单,在rename的过程中,先更改数据字典,然后调用dict_foreign_remove_from_cache
函数清理内存中的对象,这样在重新dict_load_foreigns
的时候,就一切正常了。
MySQL官方版本在5.6.23修复了这个问题。
alter 添加和删除索引导致不一致
BUG复现过程
CREATE TABLE t1(id INT, col1 INT, col2 INT, KEY(col1))ENGINE=InnoDB;
alter table t1 add key(col2), drop key col1;
复现此BUG的时候需要使用DEBUG_SYNC,在alter table的过程中,add key(col2)步骤成功,而在drop key的时候InnoDB报失败。
然后导致MySQL server层对表t1的定义和InnoDB层的定义不一致。
BUG原因分析
在drop key报引擎失败的时候,MySQL server层开始回滚整个DDL语句,server层这时回滚了FRM的定义,而InnoDB成功添加的key(col2)却没有回滚。
BUG修复方法
在alter的过程中,记录每一个阶段成功的调用,在语句结束的时候,如果遇到需要回滚的statement,需要同时回滚引擎层和server层。
因为MySQL保存了两份元数据,也造成在DDL变更的过程中,无法保证数据的一致性。