Lever's Castle

MySQL 索引调整引发的事故

December 29, 2018

前因

昨晚有个同事对线上某个数据库做了索引优化。那个表大概 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 改用别的索引,我们立马修改了代码重新发布了服务,事故逐渐恢复。

回顾

  1. Online DDL 还是很强大的,在修改表结构之前,一定要确认使用了 Online DDL,一般出问题的都不太会是 Online DDL 导致的。
  2. 线上库操作索引一定要谨慎,谨慎,再谨慎。像这种问题,如果在 MySQL 8.0 上,就可以直接用隐藏索引的方式,先看看会不会出问题。
  3. 客户端一定要限制连接数上限,防止 MySQL 出一点波动,就把连接数打满的情况

    补充知识

    什么是 MDL 锁?

    MySQL 的表级锁有两种,正常的表锁和 MDL(Meta Data Lock),MDL 不需要显示使用,访问表的时候会自动申请。

对一个表进行增删改查时,要申请 MDL 读锁; 对一个表进行表结构修改时,要申请 MDL 写锁。

Online DDL 的执行过程

  1. 拿 MDL 写锁
  2. 降级成 MDL 读锁
  3. 真正做 DDL
  4. 升级成 MDL 写锁
  5. 释放 MDL 锁

Lever

痕迹
没有过去,就没法认定现在的自己