MySQL 索引调整引发的事故
2018.12.29 11:57前因
昨晚有个同事对线上某个数据库做了索引优化。那个表大概 1000 万左右的数据吧,操作是删除一个索引,然后给另一个字段加索引。MySQL 版本 5.6,使用了 Online DDL。
删除索引前,他确认了该索引已经没有查询在使用了(只是在代码层面确认了)。之前也操作过线上数据库的索引,类似大小的表虽然会耗时多一些,但基本不会导致不可用。
然而,昨晚操作的时候,删完索引,开始加新的索引,发现 MySQL CPU 已经跑满了。第一反应是加索引导致的,因此想先把这个操作 kill 掉,结果发现连接数已经达到上限了,已经没办法连进去操作了。
运维的同学立即进行了主从切换。看上去服务恢复了。
然鹅。。。
监控发现该服务有几个接口响应变的巨慢,平均响应时间在 1s 左右。
你也许已经想到了,事故是删除索引引起的。事实上,仍然有不少读的查询还在使用那个索引,索引被删除之后,多了很多慢查询,然后才导致了 CPU 跑满。按照之前使用 Online DDL 的经验,也不应该会导致这个问题,我们从一开始就想错了。包括连接数被打满这个现象,其实一开始就应该想到,是有慢查询长时间占用连接,导致新的连接不断产生,最终把连接打满。
这个时候,我们的第一反应是把删掉的索引再加回来,但是问题又来了。了解 Online DDL 的应该都知道,开始进行 Online DDL 之前需要先申请 MDL 写锁,MDL 写锁与 MDL 读锁是冲突的,而由于不断有新的慢查询产生,MDL 读锁一直被这些慢查询占用着,导致此时无法修改表结构。
按照一般的经验,遇到这种情况会把长事务先 kill 掉再跑 alter table ,但是这个表不断有新的长事务进来,根本没机会 alter table。
只剩一个办法了,停服务,修改表结构。
就在这时,同事终于找到了慢查询代码的来源,万幸的是完全可以通过修改 sql 改用别的索引,我们立马修改了代码重新发布了服务,事故逐渐恢复。
回顾
- Online DDL 还是很强大的,在修改表结构之前,一定要确认使用了 Online DDL,一般出问题的都不太会是 Online DDL 导致的。
- 线上库操作索引一定要谨慎,谨慎,再谨慎。像这种问题,如果在 MySQL 8.0 上,就可以直接用隐藏索引的方式,先看看会不会出问题。
- 客户端一定要限制连接数上限,防止 MySQL 出一点波动,就把连接数打满的情况
补充知识
什么是 MDL 锁?
MySQL 的表级锁有两种,正常的表锁和 MDL(Meta Data Lock),MDL 不需要显示使用,访问表的时候会自动申请。
对一个表进行增删改查时,要申请 MDL 读锁; 对一个表进行表结构修改时,要申请 MDL 写锁。
Online DDL 的执行过程
- 拿 MDL 写锁
- 降级成 MDL 读锁
- 真正做 DDL
- 升级成 MDL 写锁
- 释放 MDL 锁
痕迹
没有过去,就没法认定现在的自己
