我需要在一张大约有5百万行的MySQL表上创建索引。这是一个生产表,如果我运行CREATE INDEX语句,我担心会完全阻塞一切...
有没有一种方法可以在不阻塞插入和选择的情况下创建该索引?
只是好奇我没有停下来,创建索引然后重新启动系统!
我需要在一张大约有5百万行的MySQL表上创建索引。这是一个生产表,如果我运行CREATE INDEX语句,我担心会完全阻塞一切...
有没有一种方法可以在不阻塞插入和选择的情况下创建该索引?
只是好奇我没有停下来,创建索引然后重新启动系统!
在 MySQL 5.6 及更高版本中,当创建或删除索引时,该表仍可用于读取和写入操作。CREATE INDEX 或 DROP INDEX 语句仅在访问表的所有事务完成后才完成,以便索引的初始状态反映表的最新内容。之前,在创建或删除索引时修改表通常会导致死锁,从而取消对表的 INSERT、UPDATE 或 DELETE 语句。
来自上面的答案:
"如果您使用的是大于5.1的版本,则可以在数据库在线时创建索引。因此不要担心您不会中断生产系统使用。"
这是错误的(至少对于 MyISAM / InnoDB 表格,这是99.999%的人使用的)。Clustered Edition 不同。
在创建索引时,对表执行 UPDATE 操作将会被阻塞。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 表而不阻止写入的方法,请告诉我。
通过 Google 我找到了这篇文章,其中描述了类似的技术。更好的是,在程序的相同点,他们建议一起喝酒(请注意,我在阅读该文章之前写了我的答案)!
我链接的文章谈到了一个名为pt-online-schema-change的工具,它的工作原理如下:
我自己从未尝试过这个工具。你的体验可能会有所不同。
我目前通过Amazon的RDS使用MySQL。它是一个非常方便的服务,可以封装和管理MySQL,让您只需单击一个按钮即可添加新的只读副本,并在硬件SKU之间透明地升级数据库。 你不能直接干涉数据库复制(这是福还是祸?)。但是,您可以使用只读副本提升来对只读副本进行模式更改,然后将该副本提升为新主节点。完全与我上面描述的技巧相同,只是执行起来非常容易。他们仍然没有为您提供太多关于切换的帮助。你必须重新配置和重启应用程序。
如果您确实希望迁移不会导致网站崩溃,那么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功能已经可以很好地工作了。它也有一些限制(比如与触发器不兼容),并且在模式更改正在进行时,需要将原始表中每个插入操作的写入量加倍。它会比普通的在线模式更改更大程度地占用您的磁盘,因此在只需以简单方式运行模式更改就可以正常工作的情况下,可能会“使您的网站崩溃”。 - Mark Amerypt-online-schema-change
是一个有用的工具,但在许多情况下,普通的在线 DDL 一样好,甚至更好,在少数情况下,它更好,因此对它的任何推荐都应该仔细说明,而不是普遍适用。 - Mark Amery