113

在 MySQL 中执行 ALTER TABLE 语句时,整个表在语句执行期间被读锁定(允许并发读取,但禁止并发写入)。如果它是一个大表,则 INSERT 或 UPDATE 语句可能会被阻塞很长时间。有没有办法进行“热更改”,例如添加一列以使表在整个过程中仍可更新?

大多数情况下,我对 MySQL 的解决方案感兴趣,但如果 MySQL 做不到,我会对其他 RDBMS 感兴趣。

澄清一下,我的目的只是为了避免在需要额外表列的新功能被推送到生产环境时出现停机。任何数据库模式都会随着时间而改变,这是不争的事实。我不明白为什么我们应该接受这些变化必然会导致停机。那只是虚弱。

4

19 回答 19

65

唯一的其他选择是手动执行许多 RDBMS 系统无论如何都会执行的操作...
- 创建一个新表

然后,您可以一次将旧表的内容复制到一个块上。同时始终对源表上的任何 INSERT/UPDATE/DELETE 保持谨慎。(可以通过触发器管理。虽然这会导致减速,但它不是锁定......)

完成后,更改源表的名称,然后更改新表的名称。最好在交易中。

完成后,重新编译使用该表的任何存储过程等。执行计划可能不再有效。

编辑:

一些评论认为这个限制有点差。所以我想我应该对它提出一个新的观点来说明它为什么会这样......

  • 添加新字段就像在每一行上更改一个字段。
  • 字段锁会比行锁更难,更不用说表锁了。

  • 你实际上是在改变磁盘上的物理结构,每条记录都在移动。
  • 这真的就像整个表上的更新,但影响更大......
于 2009-01-21T01:07:38.250 回答
42

Percona 制作了一个名为pt-online-schema-change的工具,可以做到这一点。

它实质上是复制表并修改新表。为了使新表与原始表保持同步,它使用触发器进行更新。这允许在后台准备新表时访问原始表。

这类似于上面 Dems 建议的方法,但这是以自动化方式进行的。

他们的一些工具有一个学习曲线,即连接到数据库,但是一旦你掌握了它,它们就是很好的工具。

前任:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
于 2012-10-10T19:23:37.780 回答
31

这个问题来自 2009 年。现在 MySQL 提供了一个解决方案:

在线 DDL(数据定义语言)

在 DDL(主要是 ALTER TABLE)操作期间提高 InnoDB 表的性能、并发性和可用性的功能。有关详细信息,请参阅第 14.11 节,“InnoDB 和在线 DDL”。

细节因操作类型而异。在某些情况下,可以在 ALTER TABLE 正在进行时同时修改表。该操作可能无需执行表复制或使用特别优化的表复制类型即可执行。空间使用由 innodb_online_alter_log_max_size 配置选项控制。

它允许您在 DDL 操作期间调整性能和并发性之间的平衡,通过选择是否完全阻止对表的访问(LOCK=EXCLUSIVE 子句)、允许查询但不允许 DML(LOCK=SHARED 子句)或允许完整查询和 DML访问表(LOCK=NONE 子句)。当您省略 LOCK 子句或指定 LOCK=DEFAULT 时,MySQL 根据操作类型允许尽可能多的并发。

尽可能就地执行更改,而不是创建表的新副本,避免临时增加磁盘空间使用量和与复制表和重建二级索引相关的 I/O 开销。

有关更多信息,请参阅MySQL 5.6 参考手册 -> InnoDB 和 Online DDL

MariaDB 中似乎也提供了在线 DDL

或者,您可以使用 ALTER ONLINE TABLE 来确保您的 ALTER TABLE 不会阻塞并发操作(不带锁)。它相当于 LOCK=NONE。

MariaDB KB 关于 ALTER TABLE

于 2016-06-18T18:45:50.280 回答
17

请参阅 Facebook 的在线架构更改工具。

http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932

不适合胆小的人;但它会完成这项工作。

于 2011-03-16T03:37:48.640 回答
14

如果可以的话,我推荐 Postgres。使用 postgres,以下过程基本上没有停机时间:

另一个很棒的特性是大多数 DDL 语句都是事务性的,因此您可以在 SQL 事务中进行整个迁移,如果出现问题,整个事情都会回滚。

我之前写过这篇文章,也许它可以对其他优点有更多的了解。

于 2011-07-01T01:25:38.113 回答
7

由于您询问了其他数据库,因此这里有一些有关 Oracle 的信息。

向 Oracle 表添加 NULL 列是一项非常快速的操作,因为它只更新数据字典。这会在很短的时间内在表上保持排他锁。但是,它将使任何依赖的存储过程、视图、触发器等无效。这些将自动重新编译。

如有必要,您可以从那里使用 ONLINE 子句创建索引。同样,只有非常短的数据字典锁。它将读取整个表以查找要索引的内容,但在执行此操作时不会阻止任何人。

如果您需要添加外键,您可以这样做并让 Oracle 相信您数据是正确的。否则它需要读取整个表并验证所有可能很慢的值(首先创建索引)。

如果您需要将默认值或计算值放入新列的每一行,则需要运行大量更新或可能填充新数据的小实用程序。这可能会很慢,尤其是当行变得更大并且不再适合它们的块时。在此过程中可以管理锁定。由于您的应用程序的旧版本仍在运行,它不知道此列,您可能需要一个偷偷摸摸的触发器或指定一个默认值。

从那里,您可以在应用程序服务器上切换到新版本的代码,它会继续运行。放下你偷偷摸摸的扳机。

或者,您可以使用 DBMS_REDEFINITION,它是一个旨在执行此类操作的黑盒。

所有这些都非常麻烦测试等等,以至于每当我们发布主要版本时,我们都会在周日早上中断。

于 2009-01-21T05:52:58.547 回答
3

如果您在进行应用程序更新时无法承受数据库的停机时间,您应该考虑维护一个双节点集群以实现高可用性。通过简单的复制设置,您可以像您建议的那样进行几乎完全在线的结​​构更改:

  • 等待所有更改在被动从站上复制
  • 将被动从站更改为主动主站
  • 对老主子做结构上的改变
  • 将更改从新主服务器复制回旧主服务器
  • 再次进行主交换和新的应用程序部署

这并不总是那么容易,但它确实有效,通常停机时间为 0!第二个节点不必只是被动节点,它可以用于测试、统计或作为后备节点。如果没有基础架构复制,可以在单台机器内设置(带有两个 MySQL 实例)。

于 2010-05-16T03:24:49.350 回答
2

没有。如果您使用的是 MyISAM 表,据我所知,它们只做表锁——没有记录锁,它们只是试图通过简单来保持一切超快。(其他 MySQL 表的操作方式不同。)在任何情况下,您都可以将表复制到另一个表,更改它,然后切换它们,更新差异。

这是一个如此巨大的改变,我怀疑任何 DBMS 都会支持它。首先能够使用表中的数据来执行此操作被认为是一种好处。

于 2009-01-21T00:25:45.493 回答
2

临时解决...

其他解决方案可能是,添加另一个具有原始表主键的表以及新列。

将主键填充到新表中并为新表中的新列填充值,并修改查询以加入此表以进行选择操作,您还需要单独插入、更新此列值。

当您能够获得停机时间时,您可以更改原始表、修改 DML 查询并删除之前创建的新表

否则,您可以使用 percona 的集群方法、复制、pt-online-schema 工具

于 2012-08-13T22:43:37.750 回答
2

你一定要试试pt-online-schema-change。我一直在使用这个工具在具有多个从属服务器的 AWS RDS 上进行迁移,它对我来说效果很好。我写了一篇详细的博客文章,介绍如何做这可能对你有帮助。

博客: http: //mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

于 2016-10-25T08:44:13.790 回答
1

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

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

您确实需要设计您的应用程序,以便不需要定期执行 ALTER TABLE 语句;您当然不希望在应用程序的正常运行期间完成任何 ALTER TABLE ,除非您准备等待或正在更改小表。

于 2009-01-21T10:04:31.300 回答
1

一般来说,答案将是“否”。您正在更改可能需要大量更新的表结构”,我绝对同意这一点。如果您希望经常这样做,那么我将提供“虚拟”列的替代方案 - 使用VIEWs用于数据的表的数量SELECT。IIRC,更改视图的定义相对轻量级,并且在编译查询计划时完成通过视图的间接性。代价是您必须将列添加到新表并制作JOIN在列中查看。

当然,这仅在您可以使用外键执行级联删除之类的情况下才有效。另一个好处是您可以创建一个包含数据组合的新表,并将视图指向它,而不会干扰客户端的使用。

只是一个想法。

于 2009-01-22T20:25:19.550 回答
1

我会推荐以下两种方法之一:

  1. 设计您的数据库表时要考虑到潜在的变化。例如,我使用过内容管理系统,它会定期更改内容中的数据字段。与其构建物理数据库结构来匹配最初的 CMS 字段要求,不如构建一个灵活的结构要好得多。在这种情况下,使用 blob 文本字段(例如 varchar(max))来保存灵活的 XML 数据。这使得结构变化非常不频繁。结构变更可能代价高昂,因此这里的成本也有好处。

  2. 有系统维护时间。系统在更改期间(每月等)脱机,并且更改安排在一天中流量最少的时间(例如凌晨 3 点到 5 点)。这些更改是在生产推出之前进行的,因此您将有一个很好的停机时间固定窗口估计。

2a。拥有冗余的服务器,这样当系统宕机时,整个站点不会宕机。这将允许您以交错的方式“滚动”更新,而不会关闭整个站点。

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

于 2009-01-22T20:33:21.937 回答
1

如果有人仍在阅读本文或碰巧来到这里,这就是使用像 mongodb 这样的 NoSQL 数据库系统的一大好处。我在处理更改表以在具有数百万行和高写入的大表上添加附加功能或索引的列时遇到了同样的问题。它最终会锁定很长时间,因此在 LIVE 数据库上执行此操作会使我们的用户感到沮丧。在小桌子上,您可以摆脱它。

我讨厌我们必须“设计我们的表格以避免改变它们”的事实。我只是认为这在当今的网站世界中行不通。您无法预测人们将如何使用您的软件,这就是您根据用户反馈快速更改内容的原因。使用 mongodb,您可以随意添加“列”而无需停机。您甚至没有真正添加它们,您只需插入带有新列的数据,它就会自动完成。

值得一试:www.mongodb.com

于 2011-07-11T02:03:33.670 回答
1

Postgres 和 MySQL 在这方面的区别在于 Postgres 不会重新创建表,而是修改数据字典,这与 Oracle 类似。因此,操作速度很快,但仍然需要在很短的时间内分配一个独占 DDL 表锁,正如其他人所说的那样。

在 MySQL 中,该操作将在阻塞事务的同时将数据复制到新表,这在 v. 5.6 之前一直是 MySQL DBA 的主要痛苦。

好消息是,自从 MySQL 5.6 发布以来,限制已经大部分解除,您现在可以享受 MYSQL DB 的真正力量。

于 2015-01-29T21:38:10.163 回答
1

正如 SeanDowney 所提到的,pt-online-schema-change是执行您在此处的问题中描述的最佳工具之一。我最近在一个实时数据库上做了很多架构更改,并且进展顺利。您可以在我的博客文章中阅读更多信息:http: //mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

于 2016-02-08T10:49:35.150 回答
0

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

如果您在电话中或在机场顺便提及餐桌名称,MyISAM 会锁定所有内容。它只是这样做...

话虽如此,锁并不是什么大不了的事。只要您不尝试为每一行添加新列的默认值,而是让它为空,并且您的存储引擎足够聪明,不会去写它,您应该可以使用仅保持足够长的时间来更新元数据。如果您确实尝试编写一个新值,那么您就完蛋了。

于 2009-01-21T05:05:28.003 回答
0

TokuDB 可以“热”添加/删除列和添加索引,该表在整个过程中是完全可用的。可通过 www.tokutek.com 获得

于 2011-11-28T02:20:07.657 回答
-7

并不真地。

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

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

于 2009-01-21T00:26:46.397 回答