第 13 / 22 篇

第 12 章:按证据缩小一次数据库故障

用户说“下单很慢”,不一定是某条 SELECT 扫描太多。请求可能在应用队列、连接池、数据库锁、执行阶段或返回网络上等待。排障先确定阶段,再提出改动。

12.1 保存能够关联的最小信息

应用侧应记录请求标识、数据库操作类别、事务阶段、错误码、总耗时与重试次数。连接池等待最好单独测量。日志中不默认写完整 SQL 参数、密码或个人数据,必要诊断应经过脱敏与访问控制。

数据库侧结合连接与事务信息、实际锁等待、执行计划和受控的慢查询记录。一个应用超时与一个数据库错误码不是同一份证据,应保留两端事实,而不是只记一行“数据库失败”。

12.2 用问题选择工具

现象 先确认 可以补充的证据
获取连接前就耗时 连接池容量与占用 池等待、活跃连接、事务时长
UPDATE 等待 谁阻塞谁 data_lock_waits、data_locks、事务与连接
查询扫描过多 实际访问路径 EXPLAIN、统计信息、返回与扫描规模
收到 1213 受害事务与锁顺序 最近死锁记录、业务事务完整性
收到 1205 超时配置与前序语句 rollback_on_timeout、当前事务状态
COMMIT 后连接异常 结果是否已提交 原业务键、可信数据库查询、恢复记录

这些工具应在权限与负载允许的条件下使用。诊断查询本身也可能消耗资源,不能把大范围抓取当成无成本操作。

12.3 每次改动只验证它能证明的事情

加索引后计划变化,应重新检查关键读写与锁行为;缩短事务后等待减少,应验证业务原子性仍成立;增加重试后错误减少,还应检查尝试放大、延迟和重复副作用。

没有真实负载实验时,不给出吞吐提升百分比。没有断电和恢复实验时,不把正常进程重启写成断电持久性证明。本书的 CI 实验证明所列事务与故障窗口,生产容量与高可用需要单独验收。

12.4 复盘保留原始事实与决策

一次好的复盘能区分症状、证据、根因、处置和长期改造。例如“终止长事务后等待消失”是处置效果;根因可能是应用在事务里等待外部接口。长期改造应缩短边界并增加相应并发回归,而不只是继续调大连接池。

练习。 一次故障通过重启应用暂时消失。请写出至少三种仍未被证明的根因假设,以及下一次发生时应提前采集的信息,避免再次仅依靠重启确认问题。