【问题标题】:Create an index on a huge MySQL production table without table locking在没有表锁定的巨大 MySQL 生产表上创建索引
【发布时间】:2011-05-13 18:45:01
【问题描述】:

我需要在约 5M 行 MySQL 表上创建索引。它是一个生产表,如果我运行 CREATE INDEX 语句,我担心会出现完整的块......

有没有办法在不阻塞插入和选择的情况下创建该索引?

只是想知道我不必停下来,创建索引并重新启动我的系统!

【问题讨论】:

  • 确保您的 myisam_sort_buffer_size 和 myisam_max_sort_file_size 足够大。

标签: mysql indexing production alter-table table-locking


【解决方案1】:

[2017] 更新:MySQL 5.6 支持在线索引更新

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

在 MySQL 5.6 及更高版本中,在创建或删除索引时,该表仍可用于读写操作。 CREATE INDEX 或 DROP INDEX 语句仅在访问表的所有事务完成后才结束,以便索引的初始状态反映表的最新内容。以前,在创建或删除索引时修改表通常会导致死锁,从而取消表上的 INSERT、UPDATE 或 DELETE 语句。

[2015] 更新表索引会阻止 MySQL 5.5 中的写入

从上面的答案:

“如果您使用大于 5.1 的版本在数据库在线时创建索引。所以不用担心您不会中断生产系统的使用。”

这是 ****FALSE****(至少对于 MyISAM / InnoDB 表,这是 99.999% 的人使用的。集群版不同。)

在创建索引时对表执行更新操作会阻塞。 MySQL 在这方面(以及其他一些事情)真的非常非常愚蠢。

测试脚本:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

我的服务器(InnoDB):

Server version: 5.5.25a Source distribution

输出(注意第 6 次操作如何阻塞完成索引更新所需的约 400 毫秒):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

与不阻塞的读取操作(交换脚本中的行注释):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

在不停机的情况下更新 MySQL 的架构

到目前为止,我只知道一种方法可以更新 MySql 架构并且不会出现可用性中断。循环大师:

  • Master A 上运行着您的 MySQL 数据库
  • 将 Master B 投入使用并让它复制来自 Master A 的写入(B 是 A 的从属)
  • 在 Master B 上执行架构更新。升级过程中会落后
  • 让 B 大师跟上。不变:您的架构更改必须能够处理从降级架构复制的命令。索引更改符合条件。简单的列添加通常符合条件。删除列?可能不是。
  • 以原子方式将所有客户端从 Master A 交换到 Master B。如果您想要安全(相信我,您会这样做),您应该确保将最后一次写入 A 复制到 B BEFORE B进行第一次写入。如果您允许同时写入 2 个以上的 master,……您会更好地理解 MySQL 复制在 DEEP 级别,否则您将走向痛苦的世界。极度痛苦。就像,你有一个 AUTOINCREMENT 的列吗???你被搞砸了(除非你在一个大师上使用偶数而在另一个上使用赔率)。不要相信 MySQL 复制“做正确的事”。它不聪明,不会拯救你。它只是比从命令行复制二进制事务日志并手动重放它们稍微不安全。尽管如此,断开所有客户端与旧主服务器的连接并将它们切换到新主服务器可以在几秒钟内完成,比等待数小时的架构升级要快得多。
  • 现在大师 B 是您的新大师。你有新的模式。生活很好。喝杯啤酒;最糟糕的时期已经过去了。
  • 与 Master A 重复此过程,升级他的架构,以便他成为您的新辅助 Master,准备好在您的主要 Master(现在是 Master B)失去电源或刚起床并死在您身上的情况下接管。李>

这不是一种更新架构的简单方法。可在严重的生产环境中工作;是的。请,请,请,如果有更简单的方法可以在不阻塞写入的情况下向 MySQL 表添加索引,请告诉我。

谷歌搜索将我带到this article,它描述了一种类似的技术。更好的是,他们建议在程序中的同一时间喝酒(请注意,我在阅读文章之前写下了我的答案)!

Percona 的 pt-online-schema-change

我在上面链接的article 谈到了一个工具pt-online-schema-change,它的工作原理如下:

  • 创建与原始结构相同的新表。
  • 更新新表的架构。
  • 在原始表上添加触发器,以便更改与副本保持同步
  • 从原始表中批量复制行。
  • 将原始表格移开并替换为新表格。
  • 放下旧表。

我自己从未尝试过该工具。 YMMV

RDS

我目前通过Amazon's RDS 使用 MySQL。这是一个非常棒的服务,它封装和管理 MySQL,让您只需一个按钮即可添加新的只读副本,并跨硬件 SKU 透明地升级数据库。真的很方便。您没有对数据库的超级访问权限,因此您不能直接使用复制(这是福还是祸?)。但是,您可以使用Read Replica Promotion 在只读从属设备上进行架构更改,然后将该从属设备提升为您的新主设备。与我上面描述的完全相同的技巧,只是更容易执行。他们仍然没有做太多的工作来帮助您进行切换。您必须重新配置并重新启动您的应用程序。

【讨论】:

  • pt-online-schema-change 即使在主从复制中也能很好地工作。我已经用它在我们的生产主数据库上的繁忙读取 20M+ 记录表上进行实时迁移,其中有 2 个复制从属,没有任何故障或停机时间。准备脚本需要一些时间,而且我通常必须创建一个包含原始 SQL 更改的 .sql 文件和一个 .sh 文件作为包装器来运行相同的 SQL,但以片段格式(没有 ALTER TABLE)。您可以使用 pt-online-schema-change 运行多个命令,方法是将它们串起来并用逗号分隔。
  • -1;我不知道旧版本,但我知道索引创建不会阻止 MySQL 5.6+ 中的并发 DML(在编写此答案时存在 RC,并且已经在 2013 年 5 月最后一次编辑此答案时正式发布),因为我依靠它在生产表上运行多小时索引创建,同时仍接受插入。虽然您可能在 5.5 及更低版本中阻止 DML 的索引创建是正确的,但此处展示的亚秒级延迟并不完全令人信服。
  • @MarkAmery - 阻塞行为就是阻塞行为,400ms 是永恒的。 MySQL 5.5 块用于索引更新。建立一个更大的测试数据库,它会阻塞几秒钟、几小时或几天。我在 MySQL 5.6 在线模式更新之前写了这篇文章,所以我的原始内容没有反映这个事实。我已经更新了帖子以反映新的可用信息。
  • @DaveDopson,您是否 100% 确定只有 UPDATE 操作被阻止?
  • 我测试的版本就是这样。
【解决方案2】:

正如blog post 所述,InnoDB ALTER TABLE 机制已针对 MySQL 5.6 进行了完全重新设计。

(有关此主题的独家概述,MySQL documentation 可以提供一个下午的阅读时间。)

要向无锁导致UPDATE/INSERT的表添加索引,可以使用以下语句格式:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;

【讨论】:

【解决方案3】:

MySQL 5.6 更新(2013 年 2 月):您现在可以在创建索引时执行读写操作,即使使用 InnoDB 表 - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

在 MySQL 5.6 及更高版本中,在创建或删除索引时,该表仍可用于读写操作。 CREATE INDEX 或 DROP INDEX 语句仅在访问表的所有事务完成后才结束,以便索引的初始状态反映表的最新内容。以前,在创建或删除索引时修改表通常会导致死锁,从而取消表上的 INSERT、UPDATE 或 DELETE 语句。

和:

在 MySQL 5.6 中,此功能变得更加通用:您可以在创建索引的同时读取和写入表,并且可以在不复制表、不阻塞 DML 操作或两者兼而有之的情况下执行更多种类的 ALTER TABLE 操作.因此,在 MySQL 5.6 及更高版本中,我们通常将这组功能称为在线 DDL,而不是快速索引创建。

来自http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

【讨论】:

  • 那如何解释戴夫的分析呢?
  • @NikhilSahu Dave 显然没有在 MySQL 5.6 上进行测试,而是在一些旧版本上进行测试。请注意,在 Dave 发布他的答案的初始修订版时,5.6 尚未发布。
  • +1。我的分析是基于 MySQL 5.5(2013 年发布的最新版本)。我正在更新我的答案以反映 MySQL 5.6 中的新功能。
【解决方案4】:

pt-online-schema-change 如果您真的想确保迁移不会导致网站瘫痪,那么您应该走的路。

正如我在上述评论中所写,我在生产中使用 pt-online-schema-change 有过几次经验。我们有 20M+ 记录的主表和一个主 -> 2 个只读复制从属。从添加新列、更改字符集到添加多个索引,我已经使用 pt-online-schema-change 完成了至少几十次迁移。我们在迁移期间也为大量流量提供服务,而且我们没有遇到任何问题。当然,在生产环境运行之前,您必须非常彻底地测试所有脚本。

我尝试将更改批处理到 1 个脚本中,以便 pt-online-schema-change 只需复制一次数据。更改列名时要非常小心,因为您会丢失数据。但是,添加索引应该没问题。

【讨论】:

  • 我不同意你对pt-online-schema-change的无条件推荐。这很棒,但是对于 MySQL 5.6+ 的在线 DDL 功能已经可以正常工作的许多情况来说,它是多余的。它也有局限性(比如不能很好地使用触发器),并且在进行模式更改时,每次插入原始表所需的写入量增加了一倍。与普通的在线架构更改相比,它会对您的磁盘造成更大的负担,因此在仅以简单的方式运行架构更改就可以正常工作的情况下,它可能会“关闭您的网站”。
  • 我是根据我当时使用 pt-online-schema-change 的实际经验写的,所以我不确定你为什么称我的推荐为“不合格”。当我运行架构更改时,在任何给定时刻,我们的网站上至少有 1000 多名访问者,当然,磁盘 IO 很费力,但我们的网站并没有宕机。拥有良好的缓存也有帮助。我没有使用 MySQL 5.6+ 在线 DDL,但根据我的经验,pt-online-schema-change 在我们的案例中做得很好。
  • @AlexYe Yikes,我的意思是“毫无保留”的“不合格”,而不是“由没有资格发表评论的人提供”的意思——我没有想到后一种解释直到我看到你的评论,当然不是我想要的!即我是说虽然pt-online-schema-change 是一个有用的工具,但在很多情况下,普通的在线 DDL 也一样好,而在少数情况下更好,所以任何关于它的建议都应该小心谨慎,而不是普遍。
猜你喜欢
  • 2021-02-23
  • 2017-12-04
  • 2021-10-15
  • 2013-06-02
  • 1970-01-01
  • 2018-01-18
  • 1970-01-01
  • 2013-08-10
相关资源
最近更新 更多