首先不讨论MySQL 5.6+版本的Online DDL特性,因为它可以比较好的兼容外键表,这里说的是OSC工具操作外键表的场景,比如使用pt-online-schema-change/gh-ost/Facebook-OSC……

以上所有的工具都绕不开一个关键的cut-over环节,即用已经修改好表结构的“影子”表替换原表,完成新旧表结构变更。以A表的OSC为例,待切换表为B,这个阶段的操作就是:rename A to A_BAK, B to A。

那如果A是一个有外键关联的表会发生什么:

  1. A为外键的父表:rename操作之后所有A的子表的FK都指向了A_BAK表,而A_BAK表数据已经静止,因此需要把rebuild所有子表的FK(重新指向A),这个操作影响程度视子表数量递增。
  2. A为外键的子表,创建B表时,MySQL在schema层级限制外键约束名称唯一,因此B的FK名称必须建个和A不一样的。这样切换后,A的FK定义相对旧A,名称不同,引用(REFERENCE)相同。

可以看到第一种场景重建所有子表索引代价非常大,而且这个操作最好是在rename完成后尽快进行,否则如果这个时候子表的写入过来,极有可能会因为子表外键指向了A_BAK导致失败,影响业务(pt-online-schema-change --alter-foreign-keys=rebuild_constraints)。同时假设子表数量很多,或者外键约束关系很复杂,这种变更相当于”牵一发动全身“。

相比之下,第二种情况没有额外操作容易了不少,只是A的外键定义变了,影响逻辑checksum的结果。

看下OSC工具实际怎么支持这两种:

pt-online-schema-change:两种都支持,第一种会在rename后自动rebuild所有子表的FK,但是这期间也可能出现上述的写入失败问题,虽然概率相对小,不过仍有风险。第二种场景则是直接在rename后表的FK名称前面增加一个下划线前缀,以示区分,绕过MySQL的限制。

gh-ost则是两种场景都不支持,见到外键直接报错,当然你可以用它来给表去掉外键……