在一个巨大的MySQL生产表上创建一个索引,而不需要表锁定

在一个巨大的MySQL生产表上创建一个索引,而不需要表锁定,mysql,indexing,production,alter-table,table-locking,Mysql,Indexing,Production,Alter Table,Table Locking,我需要在~5M行MySQL表上创建一个索引。这是一个生产表,如果我运行CREATEINDEX语句,我担心所有内容都会被完全阻塞 有没有一种方法可以在不阻止插入和选择的情况下创建索引 只是想知道我没有停止,创建索引和重新启动我的系统 [2017]更新:MySQL 5.6支持在线索引更新 在MySQL 5.6及更高版本中,在创建或删除索引时,表仍可用于读写操作。CREATEINDEX或DROPINDEX语句仅在访问表的所有事务完成后才完成,以便索引的初始状态反映表的最新内容。以前,在创建或删除索引

我需要在~5M行MySQL表上创建一个索引。这是一个生产表,如果我运行CREATEINDEX语句,我担心所有内容都会被完全阻塞

有没有一种方法可以在不阻止插入和选择的情况下创建索引

只是想知道我没有停止,创建索引和重新启动我的系统

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

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

[2015]更新MySQL 5.5中的表指示块写入 从上面的答案来看:

“如果您使用的版本大于5.1索引是在数据库联机时创建的。因此,不用担心,您不会中断生产系统的使用。”

这是****错误****(至少对于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):

输出(注意第6个操作如何在完成索引更新所需的~400ms时间内阻塞):

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

在不停机的情况下更新MySQL的模式 Thusfar,据我所知,只有一种方法可以更新MySql模式,而不会出现可用性中断。圆形母版:

  • MasterA上运行着MySQL数据库
  • 将主机B投入使用,并让它复制来自主机A的写操作(B是A的从机)
  • 在主服务器B上执行架构更新。它将在升级过程中落后
  • 让B大师跟上。不变:架构更改必须能够处理从下版本架构复制的命令。索引更改符合条件。简单的列添加通常符合条件。删除列?可能不会
  • 以原子方式将所有客户端从主服务器A交换到主服务器B。如果您希望安全(相信我,确实如此),您应该确保在B进行第一次写入之前将对A的最后一次写入复制到B。如果允许并发写入2+个主机。。。您最好从深层次理解MySQL复制,否则您将走向痛苦的世界。极度疼痛。比如,你有一个自动递增的列吗???你是完蛋了(除非你在一个母版上使用偶数,在另一个母版上使用赔率)。不要相信MySQL复制“做正确的事情”。它不聪明,也救不了你。它只是比从命令行复制二进制事务日志并手动重放它们稍微安全一些。不过,断开所有客户机与旧主机的连接并将其切换到新主机只需几秒钟,比等待一个多小时的模式升级要快得多
  • 现在大师B是你的新主人。您有了新的模式。生活是美好的。喝杯啤酒;最坏的情况已经过去了
  • 对主控台A重复此过程,升级其模式,使其成为您的新辅助主控台,准备在您的主控台(主控台B现在)断电或刚刚启动并在您身上死亡时接管
更新模式的简单方法并非如此。在恶劣的生产环境下工作;是的。请,请,请,如果有更简单的方法向MySQL表添加索引而不阻止写入,请告诉我

谷歌搜索让我想到了一种类似的技术。更妙的是,他们建议在过程中的同一点饮酒(注意,我在阅读文章之前写下了我的答案)

Percona的pt在线模式更改 上面的链接讨论了一种工具,其工作原理如下:

  • 创建与原始表具有相同结构的新表
  • 更新新表上的架构
  • 在原始表上添加触发器,以便更改与副本保持同步
  • 从原始表中批量复制行
  • 将原始表格移到一边,并替换为新表格
  • 放下旧桌子
我自己从来没有试过这个工具。YMMV

无线电数据系统 我目前通过使用MySQL。这是一个非常漂亮的服务,可以包装和管理MySQL,让您只需一个按钮就可以添加新的读取副本,并跨硬件SKU透明地升级数据库。真的很方便。您无法获得对数据库的超级访问权限,因此无法直接执行复制(这是福还是祸?)。但是,您可以使用在只读从属服务器上进行模式更改,然后将该从属服务器升级为新的主服务器。与我上面描述的完全相同的技巧,只是更容易执行。他们仍然没有为你的割伤做太多的帮助。您必须重新配置并重新启动应用程序。

正如本文所述,InnoDB
ALTER TABLE
机制已针对MySQL 5.6进行了彻底的重新设计

(对于本主题的独家概述,可以提供一个下午的阅读时间。)

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

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

如果您真的想确保迁移不会导致站点宕机,那么pt online schema change就是一个不错的选择

正如我在上面的评论中所写的那样,我在生产中有几次pt在线模式更改的经验。我们的主表有20M+记录和一个主->2个只读复制从机。我
 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
ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;