Mysql 重命名InnoDB表而不更新其外键引用?
我试图用新表替换InnoDB表,我希望所有指向旧表的外键引用都指向新表 所以我试了一下:Mysql 重命名InnoDB表而不更新其外键引用?,mysql,innodb,Mysql,Innodb,我试图用新表替换InnoDB表,我希望所有指向旧表的外键引用都指向新表 所以我试了一下: SET foreign_key_checks = 0; ALTER TABLE foo RENAME foo_old; ALTER TABLE foo_new RENAME foo; 不幸的是,即使禁用了外键检查,所有指向foo的引用也会更改为指向foo旧的。现在我正在寻找其中之一 一种在不重建整个表的情况下更改外键引用的方法,或 一种在不更新外键引用的情况下重命名表的方法 我试着删除外键并重新创建它
SET foreign_key_checks = 0;
ALTER TABLE foo RENAME foo_old;
ALTER TABLE foo_new RENAME foo;
不幸的是,即使禁用了外键检查,所有指向foo的引用也会更改为指向foo旧的。现在我正在寻找其中之一
- 一种在不重建整个表的情况下更改外键引用的方法,或
- 一种在不更新外键引用的情况下重命名表的方法李>
我试着删除外键并重新创建它们,但由于表很大,这需要几个小时。替换表的全部目的是在有限的停机时间内进行模式更改 不幸的是,我不认为有办法解决您的问题,而不首先删除外键,然后由外键创建它们 这是次要的,但我也在您的
重命名
命令中发现了一些东西。您可以将它们链接在一起,除非所有步骤都成功,否则它将回滚所有其他重命名。以下是语法:
RENAME TABLE foo TO foo_old, foo_new TO foo;
老问题,但下面是一个可能的解决方法。 基本上是移动数据,而不是重命名表。当然,您需要确保新数据符合外键规则
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;
确保将其作为一个查询运行,以便外键检查适用于整个查询。希望这能有所帮助。在MySQL 5.6上,使用
innodb_file_per_table=On
允许您动态交换表空间。这不能完全使用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\u new
复制到foo
。如果需要将foo
复制到foo\u old
,请重复这些步骤 InnoDB在外键中使用表的内部指针,因此无论为该表指定什么名称(使用RENAME
),都将保留约束,包括使用设置外键检查=0
时
一种在不重建整个表的情况下更改外键引用的方法
使用innodb\u file\u per\u table=ON
将是我们能去的最近的地方(见@vhu-answer)
一种在不更新外键引用的情况下重命名表的方法
意味着停机时间和工作量最低,并且不需要shell访问服务器的解决方案可能只是使用两个数据库,并在数据变化不大的情况下在适当的时间将其切换
如果您做了一些更改,则在切换之前,可以更快地同步应用程序中的每一个表,而不是大表或临时复制mysql命令(删除、更新、插入)。我找到了解决此问题的方法。。。您只需删除源表,而不是重命名它 对于本例,我们将该表称为“mytbl”
唯一的缺点是您无法保留原始表的备份,但您可以先将其转储。或者,如果需要原始表的逐字副本,可以创建一个额外的表副本。我注意到删除foo然后重新创建它可以保持外键的完整性。由于foo_new基本上是foo的一个副本(加上附加内容)(使用SELECT创建OUTFILE,然后加载数据填充),因此这可能是一个解决方案。我只需删除表,重新创建它,然后将数据加载到其中。只有通过重命名,我才喜欢这样一个事实,即我有一个“备份”表,以便在出现问题时使用。删除一个表会使引用保持不变(即使它们指向一个不存在的表),但在不更改引用的情况下重命名一个表是不可能的。@BartvanWissen,这种方法对你有用吗?@JamesC,
RENAME
会为你更改外键。i、 e.如果bar
引用foo
并将foo
重命名为foo\u old
,则bar
上的所有外键现在将引用foo\u old
。将重命名链接到单个语句不会改变这一点。如果表中有数百万条记录,并且执行此操作的时间很短,则此操作不可行。(想想看,第一次复制需要两个小时,截断需要一段时间,再复制两个小时。)Like不会创建外键。。你需要检查一下吗?很荒谬的是,你不能简单地重命名一个表,而不引入潜在的数小时的数据操作。无论数据库引擎需要什么,这都很糟糕。我使用SQL Server,重命名表很容易。@Triynko SQL Server在重命名外键引用的表时的行为与MySQL相同,重命名是即时的,但重命名后的表仍被FKs引用。我不确定您是否完全阅读了原始版本。有人知道2015年该版本是否有任何更新吗?替换表是相同的,还是略有修改?例如,更改是否可以由alter完成?如果是这样的话,您可能想看看。如果有FK引用,则db不允许您删除该表。至少在mariadb 10.0.36中是这样的。WRT@Ravi的观点,是的,你需要SE