MySQL不一致地更改与InnoDB表上的外键关联的索引的名称

时间:2019-05-14 13:05:21

标签: mysql sql indexing foreign-keys innodb

在删除和添加外键时,ALTER TABLE语句的行为方式似乎出现了不一致。有时关联的索引将被重命名,而其他时候则没有。我已经确定了发生这种情况的情况:


方法1

一个简单的person表,带有一个自动递增的主键id和一个外键列self_id。注意:两个单独的表的行为是相同的,我使用一个表来简化示例。

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `self_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `self_id_fk` (`self_id`),
  CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

接下来,我通过删除现有的外键并添加新的外键来重命名外键。这是在单个语句中完成的,但是如果拆分为多个ALTER TABLE语句,其行为是相同的。

ALTER TABLE `person`
  DROP FOREIGN KEY `self_id_fk`,
  ADD CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);

此语句后的表格如下:

CREATE TABLE `person` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `self_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `self_id_fk` (`self_id`),
  CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

请注意,外键已重命名,但索引尚未重命名。


APPROACH#2

执行此操作的另一种方法是首先创建没有任何外键或索引的表:

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `self_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

接下来添加外键约束:

ALTER TABLE `person` ADD CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);

这将产生下表:

CREATE TABLE `person` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `self_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `self_id_fk` (`self_id`),
  CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

请注意,由于行为described in the documentation

,系统会自动创建索引
  

...如果在索引表上自动创建索引   不存在

接下来,我以与 APPROACH#1 相同的方式重命名外键:

ALTER TABLE `person`
  DROP FOREIGN KEY `self_id_fk`,
  ADD CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);

但是这次外键和索引都已重命名:

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `self_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a_new_fk_name` (`self_id`),
  CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

有什么解释吗?几乎就像MySQL正在跟踪哪些索引是“自动创建的”,然后在更改外键时重命名它们一样。在运行ALTER TABLE语句之前,两种方法的表DDL是相同的,因此必须跟踪MySQL正在跟踪的某些“内部引擎状态”。

仅从DDL来看,无法预测运行ALTER TABLE语句时MySQL的行为方式。这意味着,一旦运行了简单的ALTER TABLE语句,两个“模式相同”的数据库可能会出现模式不匹配的情况。

1 个答案:

答案 0 :(得分:2)

我注意到了同样的事情,但是我从未见过任何解释此问题的官方文档。

我同意InnoDB似乎“知道”哪些索引是隐式创建的,哪些索引是显式创建的。但是我不知道它在哪里追踪这些信息。 InnoDB公开了INFORMATION_SCHEMA表中的许多元数据,但它必须在内部数据字典中存储更多信息。

这是有关MySQL 5.7和更低版本中内部DD的唯一文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-data-dictionary.html

我唯一的建议是,如果您需要索引名称是可预测的,则需要显式创建索引,然后创建外键约束。不要依靠外键隐式创建索引。

MySQL 8.0完全重新设计了数据字典,因此您观察到的行为可能会再次更改。 https://dev.mysql.com/doc/refman/8.0/en/data-dictionary.html