那天晚上,刚准备下班的我突然接到一通电话,语气急促:“生产挂了!”
电话那头,运维小哥焦急地喊道:“订单模块超时严重,所有请求都在排队,整个系统快瘫了!”
事件起因:一次不起眼的操作埋下隐患
事情的起因,是一个看似简单的需求:
客户希望在订单列表里增加一个字段“status”,用于显示订单状态。于是,开发同事直接在生产环境执行了一条 SQL:
ALTER TABLE orders ADD COLUMN status VARCHAR(20);
DDL 语句刚下发没多久,订单模块的响应时间开始飙升,从几十毫秒变成几秒,最终所有请求都超时,系统陷入停滞。
灾难升级:锁表问题引发的连锁反应
1. 查询队列爆满
在业务代码中,有大量针对 orders 表的读写操作:
SELECT * FROM orders WHERE status = 'pending';
UPDATE orders SET status = 'processed' WHERE id = 123;
当执行 ALTER TABLE 时,MySQL 会申请一个 MDL(元数据锁),阻止其他线程访问该表。随着业务流量持续涌入,查询队列逐渐堆积。运行 SHOW PROCESSLIST 命令,发现场景异常惨烈:
+----+-------------+-------------------+------+---------+---------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-------------------+------+---------+---------+------------------------+------------------+
| 1 | app_user | 192.168.1.1:12345 | test | Query | 300 | Waiting for table lock | SELECT * FROM... |
| 2 | root | localhost | test | Query | 290 | Waiting for table lock | UPDATE orders... |
+----+-------------+-------------------+------+---------+---------+------------------------+------------------+
2. 用户投诉不断
由于超时,订单无法正常处理,用户投诉如潮水般涌入,客服热线几乎被打爆。技术团队焦头烂额,运营部门更是压力巨大。
真相揭晓:锁表的幕后黑手
这次事故的根源是 ALTER TABLE 触发了 MySQL 的锁机制。执行 DDL 操作时,MySQL 会:
- 获取 MDL 写锁
这种锁会阻止所有线程的读写操作,直到 DDL 完成。 - 引发高并发场景下的连锁阻塞
业务的频繁查询和更新操作全部被挂起,导致锁队列快速膨胀。
避免锁表的解决方案
1. 使用 Online DDL 技术
现代 MySQL 提供了 ALGORITHM=INPLACE 的方式,允许在不锁表的情况下执行 DDL 操作:
ALTER TABLE orders ADD COLUMN status VARCHAR(20) ALGORITHM=INPLACE;
这种方式会在后台完成表结构变更,避免阻塞业务流量。
2. 分批次处理大表结构变更
对于数据量较大的表,可以采用以下方式:
- 创建一个新表,例如
orders_new,并设计新的结构:CREATE TABLE orders_new LIKE orders; - 分批次迁移数据:
INSERT INTO orders_new SELECT * FROM orders WHERE id BETWEEN 1 AND 10000; - 使用 RENAME 快速替换:
RENAME TABLE orders TO orders_backup, orders_new TO orders;
3. 设置合理的锁等待超时时间
通过调整 MySQL 的超时时间参数,避免长时间锁等待引发更大的阻塞问题:
SET innodb_lock_wait_timeout = 10;
4. 实时监控锁方法状态
通过以下命令,监控锁状态,快速定位问题:
- 使用 SHOW ENGINE INNODB STATUS 查看锁相关信息。
- 使用 performance_schema:
SELECT * FROM performance_schema.data_locks WHERE object_name = 'orders';
真实场景总结:一次小操作引发的教训
通过这次事故,我们学到以下教训:
- 千万不要在高并发的生产环境直接执行 DDL 操作。
- 尽量提前规划表结构,避免大规模变更。
- 借助 Online DDL 工具或分批迁移方式,降低锁表风险。
- 建立完善的锁监控与报警机制,及时发现和解决问题。