在没有表locking的情况下在一个巨大的MySQL生产表上创build索引

我需要在〜5M行的MySQL表上创build一个索引。 这是一个生产表,如果我运行一个CREATE INDEX语句,我就担心一切的一切

有没有一种方法来创build索引,而不阻塞插入和select?

只是想知道我不停止,创build索引,并重新启动我的系统!

编辑:下面的答案涉及重新启动服务器后,添加一个索引,更详细地阅读这个问题,不是这个调查的核心话题 – 戴夫其实是正确的,应该是被接受的答案。

如果您使用的版本大于5.1,则在数据库联机时创build索引。 所以不用担心,不会中断生产系统的使用。

看到下面的MySQL 票据作为参考。

从MySQL 5.1.7开始,ADD INDEX和DROP INDEX操作是在索引仅在可变宽度列上时在线执行的。 资源

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

https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

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

[2015]更新表格标记块在MySQL 5.5中写入

从上面的答案:

“如果在数据库在线的时候创build了大于5.1的版本,那么不用担心不会中断生产系统的使用。

这是**** FALSE **** (至less对于MyISAM / InnoDB表,这是99.999%的人使用的,Clustered Edition是不同的)。

在创build索引时,对表执行UPDATE操作将会失败 。 对于这个(还有一些其他的东西),MySQL真的很愚蠢。

testing脚本:

( 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 

输出(注意第六个操作如何阻塞~400ms才能完成索引更新):

  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 

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

  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的Schema

因此,只有一个我知道的更新MySql模式的方法,不会遭受可用性中断。 圆形大师:

  • 主A有你的MySQL数据库上运行
  • 带上B主服务器,并复制主A的写入(B是A的奴隶)
  • 在主B上执行模式更新。在升级过程中会落后
  • 让B老师赶上。 不变:您的模式更改必须能够处理从逆向模式复制的命令。 索引更改合格。 简单的列添加通常是合格的。 删除列? 可能不会。
  • 原则上将所有客户端从主服务器A交换到主服务器B.如果您想要安全(相信我),则应确保最后一次写入A被复制到B BEFORE B首次写入之前。 如果你允许并发写入2+主,…你可以更深入地了解在DEEP级别的MySQL复制,或者你正在走向一个痛苦的世界。 极端的痛苦。 像,你有一列是AUTOINCREMENT? 你被搞砸了(除非你在一个主人身上使用偶数,另一个主人使用赔率)。 不要相信MySQL复制“做正确的事情”。 这不聪明,不会救你。 这比从命令行复制二进制事务日志并手动重放它们稍微安全一些。 尽pipe如此,断开所有客户端与旧主服务器的连接,并将它们切换到新主服务器上可以在几秒钟内完成,比等待多小时模式升级要快得多。
  • 现在B老师是你的新主人 你有新的模式。 生活很好。 喝杯啤酒 最糟糕的是结束了。
  • 重复这个过程与主A,升级他的架构,使他成为你的新的二级主,准备接pipe,如果你的主要的主人(现在的主B)失去能力或刚刚死了你。

更新模式的简单方法不是。 在严肃的生产环境中工作; 是的。 请,请,如果有一个更简单的方式添加索引到MySQL表没有阻止写道,让我知道。

谷歌search引导我这篇文章 ,描述了一个类似的技术。 更好的是,他们build议在同一时间饮用(请注意,我在阅读文章前写下了答案)!

Percona的在线模式改变

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

  • 创build与原始结构相同的新表。
  • 在新表上更新模式。
  • 在原始表上添加一个触发器,以便更改与副本保持同步
  • 从原始表中批量复制行。
  • 移动原始表格,并用新表格replace。
  • 放下旧桌子。

我从来没有尝试过这个工具。 因人而异

RDS

我目前正在通过亚马逊的RDS使用MySQL。 这是一个非常漂亮的服务,它包装和pipe理MySQL,允许您用一个button添加新的只读副本,并通过硬件SKU透明地升级数据库。 真的很方便 你没有超级访问数据库,所以你不能直接与复制(这是一个祝福或诅咒?)。 但是,您可以使用只读副本在只读从属设备上更改模式,然后将该从属设备升级为新的主设备。 和我上面描述的完全一样的技巧,只是更容易执行。 他们仍然没有太多的帮助你切入。 您必须重新configuration并重新启动您的应用程序。

正如这篇博客文章所概述的,InnoDB ALTER TABLE机制已经完全针对MySQL 5.6进行了重新devise。

(关于这个主题的独家概述, MySQL文档可以提供一个下午的阅读价值。)

要将索引添加到不带 UPDATE / INSERT 的锁的表中,可以使用以下语句格式:

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

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

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

和:

在MySQL 5.6中,这个特性变得更加通用:在创build索引时可以读写表,并且可以在不复制表的情况下执行更多types的ALTER TABLE操作,而不会阻止DML操作,或者两者兼而有之。 因此,在MySQL 5.6及更高版本中,我们通常将这组function称为在线DDL,而不是快速索引创build。

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

如果您确实希望确保迁移不会导致网站无法正常运行,则可以使用pt-online-schema-change。

正如我在上面的评论中所写的那样,我在生产中有一些在线模式转换的经验。 我们有我们的20M +logging主表和一个主 – > 2只读复制从站。 我已经做了至less十几个迁移与pt-online-schema-从添加一个新的列,改变字符集,到添加多个索引。 我们在迁徙期间也有很多的交通,我们没有任何的呃逆。 当然,在生产之前,你必须非常彻底地testing所有的脚本。

我尝试将更改批量化为1个脚本,以便pt-online-schema-change只需复制一次数据。 并且要更改列名称,因为您将丢失数据。 但是,添加索引应该没问题。