前言
12月19日晚,项目组准备发布新版本,在执行SQL(DDL语句)的时候,生产环境大量接口报错,甚至阻塞了核心交易链路。
不一会功夫,飞书工作群立马炸开了锅,各种投诉和问责接踵而至.....
事故过程回顾
- 19日17:00,开发人员和DBA(数据库管理员)沟通本次版本执行SQL(DDL)对系统的影响,估算执行时间为几分钟,因为之前执行过类似语句
- 19日17:30,开发人员提交SQL工单,DBA审批通过,SQL待手动执行
- 19日21:35,开发人员手动执行SQL
- 19日21:40,生产环境开始出现大量告警和用户反馈故障,此时SQL还在执行中
- 19日21:52,SQL还在执行中,DBA直接杀死SQL执行进程
- 19日21:55,生产环境逐步恢复正常
事故原因分析
数据库在执行DDL的时候,发生了锁表,阻塞了大量正常业务的增删改查,大量线程在等待锁,接着dubbo线程池被打满,最终导致生产环境大批系统故障,下图是当时生产环境的日志:
此外,执行SQL的时间为晚上21:35,这个时间并非系统流量低峰期,数据库需要处理大量用户的请求;而且,本次操作的表还是张大表,数据量上千万,无疑给DDL增加了难度和风险。
本次DDL的执行方式为直接执行SQL,生产环境MySQL版本为5.7,也就是说最终使用的是MySQL自带的Online DDL机制,这种机制有可能会阻塞DML语句(数据增删改查)。
Online DDL为什么会锁表
执行DDL的方式有哪些
对于MySQL 5.5,执行DDL会阻塞DML语句。
到了MySQL 5.6,官方引入了Online DDL,顾名思义是可以在线上环境执行的一种方式,但是不代表它完全不会阻塞DML。
后来又衍生了一些第三方DDL工具,如pt-osc、github的gh-ost,它们相比较MySQL自带的方式更加安全。
Online DDL的具体执行流程
对于MySQL 5.7,Online DDL 包含了 copy 和 inplace 两种算法。
copy算法就跟MySQL 5.5一样会全程阻塞DML。
对于inplace算法,执行过程中会有小部分时间获取MDL写锁造成阻塞DML。 与此同时,还区分两类方式,一类需要重建表(重新组织记录),比如 optimize table 、添加索引、添加/删除列、修改列 NULL / NOT NULL 属性等;另外一类只需要修改表的元数据,比如删除索引、修改列名、修改列默认值、修改列自增值等。MySQL 将这两类方式分别称为 rebuild 方式和 no-rebuild 方式。
Online DDL过程包括3个阶段,prepare阶段、ddl执行阶段、commit阶段,具体流程如下图所示。通常情况下,在prepare和commit阶段占用MDL写锁(图中exclusive-mdl锁)的时间会非常短,但是如果是面对大表DDL或者是增删改查比较频繁的情况下,发生异常的风险增大,可能会造成长时间阻塞DML。
总结
- DDL需要在系统流量低峰期执行,尤其是大表
- MySQL 5.7的Online DDL机制会用到MDL写锁,因此有阻塞DML的可能
- 面对复杂情况,想采取更安全的手段,可以使用DDL操作工具如pt-osc、github的gh-ost
参考
MySQL 5.7 官方文档 dev.mysql.com/doc/refman/…