[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 在只读从属设备上进行架构更改,然后将该从属设备提升为您的新主设备。与我上面描述的完全相同的技巧,只是更容易执行。他们仍然没有做太多的工作来帮助您进行切换。您必须重新配置并重新启动您的应用程序。