背景

MySQL保存了两份元数据,一份在server层,保存在FRM文件中,另外一份在引擎层,比如InnoDB的数据字典中,这样也就造成了DDL语句经常导致元数据不一致的情况,下面介绍两个近期出现的因为DDL产生的bug。

rename 外键引用的column

BUG复现过程

  1. CREATE TABLE t1 (a INT NOT NULL,
  2. b INT NOT NULL,
  3. INDEX idx(a)) ENGINE=InnoDB;
  4. CREATE TABLE t2 (a INT KEY, b INT, INDEX ind(b),
  5. FOREIGN KEY (b) REFERENCES t1(a)
  6. ON DELETE CASCADE
  7. ON UPDATE CASCADE) ENGINE=InnoDB;
  8. 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复现过程

  1. CREATE TABLE t1(id INT, col1 INT, col2 INT, KEY(col1))ENGINE=InnoDB;
  2. 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变更的过程中,无法保证数据的一致性。