我正在尝试用新表替换InnoDB表,并且我希望所有指向旧表的外键引用指向新表。
所以我尝试了这个:
SET foreign_key_checks = 0;
ALTER TABLE foo RENAME foo_old;
ALTER TABLE foo_new RENAME foo;
不幸的是,即使禁用了foreign_key_checks,指向foo的所有引用都会更改为指向foo_old。现在我正在寻找
我尝试删除外键并重新创建它们,但由于表格很大,所以需要几个小时。替换表的重点是在有限的停机时间内进行模式更改。
答案 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.ibd
,foo_new.cfg
和foo_new.frm
。将它们分别复制到foo.ibd
,foo.cfg
和foo.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'。
唯一的缺点是你无法保留原始表的备份,但你可以事先将mysqldump保留。或者,如果您想要原始表的逐字副本,则可以创建其他表副本。