不锁定表的情况下进行ALTER TABLE操作?

118

在MySQL中执行ALTER TABLE语句时,整个表会被读锁定(允许并发读取但禁止并发写入)直到该语句执行完成。如果这是一个大表格,则INSERT或UPDATE语句可能会被阻塞很长时间。有没有一种方法可以进行“热修改”,比如以这样的方式添加列,使得整个过程中表格仍然可更新?

我主要关心的是MySQL的解决方案,但如果MySQL不能实现,我也对其他关系型数据库管理系统感兴趣。

澄清一下,我的目的只是为了避免 downtime,因为我们需要在生产中推出需要额外表格列的新功能。任何数据库模式都将随着时间的推移而改变,这只是生活中的事实。我不明白为什么我们应该接受这些更改必须不可避免地导致停机时间;那太弱了。


2
不禁想问你会修改表多少次? - Allain Lalonde
1
在我看来,数据库模式更改通常伴随着全新版本 - 它们不像其他更改那样零散地推出。这无疑是一件大事。 - dkretz
13
“@AllainLalonde - 多次询问这个问题是合理的,特别是如果系统停机会危及生命或导致大量损失。无论如何,新的软件需求有时会出现。” - Nathan Long
19个回答

1
如果有人仍在阅读此内容或偶然来到这里,使用NoSQL数据库系统(如mongodb)的最大好处就在于此。我曾遇到过相同的问题,需要修改表格以添加额外功能的列或对具有数百万行和高写入的大型表格创建索引。这将导致锁定时间非常长,因此在LIVE数据库上执行此操作会让我们的用户感到沮丧。在小表格上,您可以轻松应对。
我讨厌我们必须“设计我们的表格以避免修改它们”的事实。我认为这在今天的网站世界中行不通。您无法预测人们将如何使用您的软件,这就是为什么基于用户反馈快速更改事物的原因。使用mongodb,您可以随意添加“列”,而无需停机。您甚至不需要添加它们,只需插入具有新列的数据即可自动完成。
值得一看:www.mongodb.com

2
MySQL仍然被许多系统使用,所以问题实际上是关于如何在SQL RDBMS中实现模式更改,尽管我也是NoSQL的忠实支持者。 - Alexy

1

1

使用Innodb插件,仅添加或删除次要索引的ALTER TABLE语句可以“快速”完成,即无需重建表。

一般来说,在MySQL中,任何ALTER TABLE都涉及重建整个表,这可能需要很长时间(即如果表中有大量有用的数据)。

您真的需要设计应用程序,以便不需要经常执行ALTER TABLE语句;除非您准备等待或更改微小的表,否则在应用程序正常运行期间不要执行任何ALTER TABLE。


1
一般来说,答案往往是“不行”。你正在改变表的结构,这可能需要大量的更新,我完全同意这一点。如果你希望经常这样做,那么我会提供一个替代“虚拟”列的方法——使用VIEW而不是表来SELECT数据。如果我没记错的话,更改视图的定义相对轻量级,通过视图进行间接查询是在查询计划编译时完成的。代价是你必须将列添加到新表中,并使视图JOIN该列。
当然,这仅适用于您可以使用外键执行级联删除等操作的情况。另一个好处是,您可以创建一个包含数据组合的新表,并将视图指向它,而不会干扰客户端使用。
只是一个想法。

1
我会建议两种方法:
1. 设计数据库表时需要考虑到潜在的更改。例如,我曾经与内容管理系统一起工作,这些系统经常更改内容数据字段。因此,建立物理数据库结构以匹配最初的 CMS 字段要求不如建立一个灵活的结构。在这种情况下,使用一个 blob 文本字段(例如 varchar(max))来保存灵活的 XML 数据。这使得结构更改非常少见。结构更改可能很昂贵,所以这里也有成本效益。
2. 安排系统维护时间。系统在更改期间离线(例如每月),并且在一天中交通最不繁忙的时间(例如凌晨 3-5 点)安排更改。在生产推出之前,对更改进行分阶段,这样您就可以估计停机时间。
2a. 拥有冗余服务器,这样当系统停机时,整个站点不会崩溃。这将允许您以错开的方式推出更新,而不会导致整个站点停机。

选项2和2a可能不可行;它们往往只适用于较大的网站/操作。然而,它们是有效的选择,我个人使用过这里提供的所有选项。


在选项1中,我们可以添加一个名为“其他”的通用字段,类型为JSON,并将其用于我们想要添加的任何字段。 - vrtx54234

1
Postgres和MySQL在这方面的区别在于,Postgres不会重新创建表,而是修改类似于Oracle的数据字典。因此,操作速度很快,但仍需要为非常短的时间分配一个独占的DDL表锁,正如其他人所述。
在MySQL中,该操作将复制数据到新表,同时阻止事务,这一直是MySQL DBA在v.5.6之前的主要问题。
好消息是,自从MySQL 5.6发布以来,限制已经被大部分取消,现在您可以享受MYSQL DB的真正力量了。

3
看起来你试图链接到一个关于MySql 5.6变更的参考资料,但是它没有生效。请再试一次。 - dg99

0

如果您可以预测虚拟列的类型(并使它们可为空),那么虚拟列是一个好主意。请检查您的存储引擎如何处理空值。

如果您甚至在电话中提到表名,或者在机场说了一声,MyISAM 将锁定所有内容。 它就是这样...

话虽如此,锁并不是真正的大问题;只要您不尝试为每行添加新列的默认值,而是让它保持为空,并且您的存储引擎足够聪明,不会去写入它,那么您应该可以使用仅持有足够长时间以更新元数据的锁,这样就可以了。 如果您尝试写入新值,那么您就完蛋了。


1
我尝试向一个InnoDB表添加一个NULL列,结果需要重建整个表;这不是一个简单的“更新元数据”的操作。 - Daniel
我认为这个想法是在设计数据库时包括额外的可空列,这样如果需要新功能,就可以通过开始使用新列来“添加”新列。 它可能没有一个好听的名称,但如果正确选择/预测数据类型,它应该能够工作。 - supercat

0

TokuDB 可以「热」地添加/删除列和索引,整个过程中表格完全可用。可通过 www.tokutek.com 访问。


-7

不完全如此。

毕竟,您正在更改表的基础结构,这是对基础系统非常重要的信息。您还(可能)在磁盘上移动大量数据。

如果您打算经常这样做,最好使用“虚拟”列填充表,以供将来使用。


3
在表格中填充虚拟列似乎是一个非常糟糕的主意。 - Jost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接