SQL运维过程中,线上事故通常源于开发规范缺失、监控不到位或变更管控不严。对典型事故进行复盘,有助于团队识别风险并建立防御机制。

一、慢查询拖垮数据库
某业务接口响应突然变慢,数据库CPU升至100%。排查发现一条统计SQL未走索引,在千万级表上全表扫描。
事故代码还原
-- 错误写法:对create_time使用函数,导致索引失效 SELECT COUNT(*) FROM order_info WHERE DATE(create_time) = '2023-08-01';
优化方式
-- 正确写法:范围查询可使用索引 SELECT COUNT(*) FROM order_info WHERE create_time >= '2023-08-01 00:00:00' AND create_time < '2023-08-02 00:00:00';
二、误删生产数据
运维人员在清理测试数据时,错连生产库执行DELETE未加WHERE条件,导致用户表被清空。
风险命令示例
-- 高危操作:无条件的删除 DELETE FROM user_profile;
防范手段包括:生产操作必须使用工单审批、禁止直连数据库手工执行写操作、定期备份并验证可恢复性。
三、索引缺失引发全表扫描
新上线功能按用户ID查询订单,但漏建索引,流量上涨后数据库负载飙升。
| 问题点 | 说明 |
|---|---|
| 缺失索引 | user_id列无索引,查询走全表扫描 |
| 影响范围 | 订单详情页超时,支付回调堆积 |
补建索引
ALTER TABLE order_info ADD INDEX idx_user_id (user_id);
四、事务未提交造成锁等待
后台脚本开启事务后忘记提交,持有行锁不释放,前端更新被阻塞。
问题伪代码
// 错误示例:未关闭事务
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement("UPDATE account SET balance=balance-? WHERE id=?");
ps.setBigDecimal(1, amount);
ps.setLong(2, userId);
ps.executeUpdate();
// 缺少 conn.commit(); 导致锁一直占用
复盘总结
- 建立SQL审核机制,上线前检查执行计划
- 核心表操作必须带WHERE并先SELECT确认
- 完善监控告警,慢查询和锁等待及时通知
- 定期组织事故复盘,沉淀为团队规范
SQL运维的 stability 来自细节管控,每一次事故都是改进流程的机会。