我需要在 ~5M 行 MySQL 表上创建索引。它是一个生产表,如果我运行 CREATE INDEX 语句,我担心会出现所有内容的完整块......
有没有办法在不阻塞插入和选择的情况下创建该索引?
只是想知道我不必停下来,创建索引并重新启动我的系统!
我需要在 ~5M 行 MySQL 表上创建索引。它是一个生产表,如果我运行 CREATE INDEX 语句,我担心会出现所有内容的完整块......
有没有办法在不阻塞插入和选择的情况下创建该索引?
只是想知道我不必停下来,创建索引并重新启动我的系统!
在 MySQL 5.6 及更高版本中,在创建或删除索引时,该表仍可用于读取和写入操作。CREATE INDEX 或 DROP INDEX 语句仅在访问表的所有事务完成后才结束,以便索引的初始状态反映表的最新内容。以前,在创建或删除索引时修改表通常会导致死锁,从而取消表上的 INSERT、UPDATE 或 DELETE 语句。
从上面的答案:
“如果您使用的版本大于 5.1 索引是在数据库在线时创建的。所以不用担心,您不会中断生产系统的使用。”
这是 ****FALSE****(至少对于 MyISAM / InnoDB 表,这是 99.999% 的人使用的。集群版不同。)
在创建索引时对表执行 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 表添加索引,请告诉我。
谷歌搜索将我带到描述类似技术的这篇文章。更好的是,他们建议在程序的同一点喝酒(请注意,我在阅读文章之前写了我的答案)!
我在上面链接的文章谈到了一个工具pt-online-schema-change,它的工作原理如下:
我自己从未尝试过该工具。YMMV
我目前正在通过Amazon 的 RDS使用 MySQL 。这是一个非常棒的服务,它封装和管理 MySQL,让您只需一个按钮即可添加新的只读副本,并跨硬件 SKU 透明地升级数据库。真的很方便。您没有对数据库的超级访问权限,因此您不能直接使用复制(这是福还是祸?)。但是,您可以使用只读副本提升在只读从属设备上更改架构,然后将该从属设备提升为您的新主设备。与我上面描述的完全相同的技巧,只是更容易执行。他们仍然没有做太多的工作来帮助您进行切换。您必须重新配置并重新启动您的应用程序。
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
pt-online-schema-change 如果您真的想确保迁移不会导致站点瘫痪,那么您可以这样做。
正如我在上述评论中所写,我在生产中使用 pt-online-schema-change 有过几次经验。我们有 20M+ 记录的主表和一个主 -> 2 个只读复制从属。从添加新列、更改字符集到添加多个索引,我已经使用 pt-online-schema-change 完成了至少几十次迁移。我们在迁移期间也为大量流量提供服务,而且我们没有遇到任何问题。当然,在生产运行之前,您必须非常彻底地测试所有脚本。
我尝试将更改批处理到 1 个脚本中,以便 pt-online-schema-change 只需复制一次数据。更改列名时要非常小心,因为您会丢失数据。但是,添加索引应该没问题。