ALTER TABLE没有锁定表?

时间:2009-01-21 00:22:42

标签: sql mysql ddl alter-table

在MySQL中执行ALTER TABLE语句时,整个表在语句的持续时间内被读锁定。如果它是一个大表,那意味着插入或更新语句可能会被锁定一段时间。有没有办法进行“热改变”,比如添加一个列,使得表在整个过程中仍然可以更新?

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

为了澄清,我的目的只是为了避免在需要额外表格列的新功能推向生产时停机。任何数据库模式随着时间的推移而发生变化,这只是生活中的一个事实。我不明白为什么我们应该接受这些变化必然导致停工;那只是很弱。

19 个答案:

答案 0 :(得分:58)

唯一的另一个选择是手动执行许多RDBMS系统所做的事情......
- 创建一个新表

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

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

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

修改

有人对这个限制有点评论。所以我想我会对它进行一个新的观点来说明它为什么会这样......

  • 添加新字段就像更改每一行上的一个字段一样。
  • Field Locks比Row锁更难,更别提了桌锁
  • 您实际上正在更改磁盘上的物理结构,每个记录都会移动。
  • 这真的就像整张桌子上的更新,但影响更大......

答案 1 :(得分:39)

Percona制作了一个名为pt-online-schema-change的工具,可以完成此任务。

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

这类似于上面提出的Dems建议方法,但这是以自动方式实现的。

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

例如:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends

答案 2 :(得分:19)

这个问题来自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 Reference Manual -> InnoDB and Online DDL了解详情。

似乎在线DDL也可以在MariaDB中使用

  

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

MariaDB KB about ALTER TABLE

答案 3 :(得分:17)

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

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

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

答案 4 :(得分:14)

如果这是一个选项,我推荐Postgres。使用postgres,以下程序基本上没有停机时间:

其他一些很棒的功能是大多数DDL语句都是事务性的,所以你可以在SQL事务中进行整个迁移,如果出现问题,整个事件都会被回滚。

我之前写过this,也许它可以对其他优点有更深入的了解。

答案 5 :(得分:7)

由于您询问了其他数据库,以下是有关Oracle的一些信息。

将一个NULL列添加到Oracle表是一种非常快速的操作,因为它只更新数据字典。这可以在很短的时间内对桌子进行独占锁定。但是,它将使任何depedant存储过程,视图,触发器等无效。这些将自动重新编译。

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

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

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

从那里,您可以在应用程序服务器上执行switcharoo到新版本的代码,它将继续运行。放下偷偷摸摸的触发器。

或者,您可以使用DBMS_REDEFINITION,这是一个专门用于执行此类操作的黑盒子。

所有这些都非常麻烦,以至于每当我们发布一个主要版本时,我们就会在周日早上停电。

答案 6 :(得分:3)

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

  • 等待所有更改在被动从属服务器上复制
  • 将被动奴隶改为活跃主人
  • 对旧主人进行结构性更改
  • 将更改从新主服务器复制回旧主服务器
  • 再次进行主交换,同时进行新的应用部署

这并不总是很容易,但它可以正常工作,通常有0停机时间!第二个节点不必只是被动节点,它可以用于测试,统计或作为回退节点。 如果没有基础架构复制,可以在一台机器上设置(有两个MySQL实例)。

答案 7 :(得分:2)

不。如果你正在使用MyISAM表,我最好的理解他们只做表锁 - 没有记录锁,他们只是试图通过简单性保持一切超快。 (其他MySQL表的运行方式不同。)在任何情况下,您都可以将表复制到另一个表,更改它,然后切换它们,更新差异。

这是一次如此大规模的改动,我怀疑任何DBMS都会支持它。首先,能够使用表格中的数据来实现它是一种好处。

答案 8 :(得分:2)

临时解决方案......

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

将主键填充到新表中,并在新表中填充新列的值,并修改查询以将此表连接到选择操作,还需要为此列值单独插入和更新。

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

否则,您可以从percona获取聚类方法,复制,pt-online-schema工具

答案 9 :(得分:1)

你一定要试试pt-online-schema-change。我一直在使用这个工具在AWS RDS上与多个奴隶进行迁移,它对我来说非常有效。我写了一篇精心制作的博客文章,介绍如何做到这一点可能对你有所帮助。

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

答案 10 :(得分:1)

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

答案 11 :(得分:1)

Postgres和MySQL在这方面的区别在于它在Postgres中不会重新创建表,而是修改类似于Oracle的数据字典。因此,操作很快,而如上所述,其他人仍需要在非常短的时间内分配独有的DDL表锁。

在MySQL中,操作会在阻止事务的同时将数据复制到新表,这对于v.5.6之前的MySQL DBA来说是主要的痛苦。

好消息是,自MySQL 5.6发布以来,限制已经mostly lifted,您现在可以享受MYSQL数据库的真正威力了。

答案 12 :(得分:1)

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

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

值得一试:www.mongodb.com

答案 13 :(得分:1)

我建议采用以下两种方法之一:

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

  2. 有系统维护时间。系统在更改期间(每月等)脱机,并且在一天中交通最少的时间(例如,上午3-5点)安排更改。这些更改将在生产推出之前暂存,因此您可以很好地估算停机时间。

  3. 2a上。拥有冗余服务器,这样当系统停机时,整个站点都不会停机。这将允许您以交错的方式“滚动”您的更新,而不会占用整个网站。

    选项2和2a可能不可行;它们往往只适用于较大的网站/运营。但是,它们是有效的选项,我个人使用了此处提供的所有选项。

答案 14 :(得分:1)

一般来说,答案是“不”。你正在改变表的结构,这可能需要大量的更新“我绝对同意。如果你希望经常这样做,那么我将提供”虚拟“列的替代方法 - 使用{{ 1}}而不是VIEW数据的表.IIRC,更改视图的定义是相对轻量级的,并且通过视图的间接是在编译查询计划时完成的。费用是你必须的将列添加到新表中,并在列中创建视图SELECT

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

只是一个想法。

答案 15 :(得分:1)

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

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

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

答案 16 :(得分:0)

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

答案 17 :(得分:0)

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

如果您在电话中,在机场传递表名,MyISAM将锁定所有内容。它就是这样......

话虽如此,锁定并不是那么重要;只要你没有尝试将新列的默认值添加到每一行,但是让它保持为空,并且你的存储引擎足够智能而不去编写它,你应该只使用一个锁定持有足够长的时间来更新元数据。如果你确实试着写一个新值,那么你就是吐司。

答案 18 :(得分:-6)

不是。

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

如果您计划这么做,那么最好只使用“虚拟”列填充表格,以备将来使用。