重命名InnoDB表而不更新外键引用?

时间:2011-06-17 11:00:11

标签: mysql innodb

我正在尝试用新表替换InnoDB表,并且我希望所有指向旧表的外键引用指向新表。

所以我尝试了这个:

SET foreign_key_checks = 0;
ALTER TABLE foo RENAME foo_old;
ALTER TABLE foo_new RENAME foo;

不幸的是,即使禁用了foreign_key_checks,指向foo的所有引用都会更改为指向foo_old。现在我正在寻找

  • 一种在不重建整个表的情况下更改外键引用的方法,或者
  • 一种在不更新外键引用的情况下重命名表的方法。

我尝试删除外键并重新创建它们,但由于表格很大,所以需要几个小时。替换表的重点是在有限的停机时间内进行模式更改。

5 个答案:

答案 0 :(得分:11)

旧问题,但以下是可能的解决方法。 基本上移动数据而不是重命名表。当然,您需要确保新数据符合外键规则。

SET foreign_key_checks = 0;
CREATE TABLE IF NOT EXISTS foo_old LIKE foo;
INSERT INTO foo_old SELECT * FROM foo;
TRUNCATE foo;
INSERT INTO foo SELECT * FROM foo_new;

确保将其作为一个查询运行,以便foreign_key_checks适用于整个事务。希望这会有所帮助。

答案 1 :(得分:7)

不幸的是,我不认为在没有先删除外键而是重新创建外键的情况下,可以解决问题。

这是次要的,但我也发现了RENAME命令。您可以将它们链接在一起,除非所有步骤都成功,否则它将回滚所有其他重命名。这是语法:

RENAME TABLE foo TO foo_old, foo_new TO foo;

答案 2 :(得分:7)

在带有innodb_file_per_table=ON的MySQL 5.6上,您可以动态交换表空间。这不能完全使用SQL来完成,因为文件操作需要单独执行。首先准备要复制的foo_new表并删除foo数据:

SET foreign_key_checks = 0;
ALTER TABLE foo DISCARD TABLESPACE;
FLUSH TABLES foo_new FOR EXPORT;

此时,您需要将相关的InnoDB文件复制到正确的名称。文件存储在数据目录中。例如,在Debian上,默认情况下它们位于/var/lib/mysql/yourdatabase,文件为foo_new.ibdfoo_new.cfgfoo_new.frm。将它们分别复制到foo.ibdfoo.cfgfoo.frm。例如:

$ cp foo_new.ibd foo.ibd
$ cp foo_new.frm foo.frm
$ cp foo_new.cfg foo.cfg

注意MySQL可以访问新文件(例如,他们拥有正确的所有者,访问权限)。完成后,您可以再次导入表并启用外键:

UNLOCK TABLES;
ALTER TABLE foo IMPORT TABLESPACE;
SET foreign_key_checks = 1;

这只会将foo_new复制到foo。如果您需要将foo复制到foo_old,请重复这些步骤。

答案 3 :(得分:5)

InnoDB使用外键中表的内部指针,因此无论给予此表的名称(使用RENAME),都将保留约束,包括使用SET foreign_key_checks = 0时。

  

一种在不重建整个表的情况下更改外键引用的方法

使用innodb_file_per_table=ON将是我们最接近的(见@vhu答案)。

  

一种重命名表而不更新外键引用的方法。

解决方案意味着最短的停机时间和工作量,并且不需要shell访问服务器,可能只是使用两个数据库,并按时启用它们,如果数据不会发生太大变化

在应用程序中同步每个其他表或者在应用程序中临时复制mysql命令(删除,更新,插入)可能更快,直到切换时如果你有一些更改。

答案 4 :(得分:2)

我找到了解决方法...你只需删除源表而不是重命名它。

对于这个例子,我们将调用表'mytbl'。

  1. 创建源表的副本,例如'mytbl_new'
  2. 将数据复制到新表
  3. 删除源表'mytbl'
  4. 将'mytbl_new'重命名为'mytbl'
  5. 唯一的缺点是你无法保留原始表的备份,但你可以事先将mysqldump保留。或者,如果您想要原始表的逐字副本,则可以创建其他表副本。