一次数据库等待链事故里,面板到底该怎么帮你收敛判断
真正麻烦的数据库等待链事故,不是没有图,而是现场每个人都能从图里讲出一个像样的猜测。沿一次典型夜间事故往下走,才能看清哪些面板只是证据,哪些误判最该先排掉。
数据库等待链最难受的地方,不是数据库一眼打满,也不是某条 SQL 明晃晃地慢到藏不住。
真正麻烦的现场,往往是这样开始的:
- 几个核心接口的 RT 先抬起来了
- 超时开始零星出现,但还没到整站雪崩
- 连接池 active 往上顶,pending 也在涨
- 数据库 RT 比平时高了一截
- 慢 SQL 数量变多了,但又没多到足够“一锤定音”
- 锁等待有一点,缓存命中率也掉了一点
这时候群里最容易出现的,不是没有判断,而是判断太多:
- “数据库 CPU 还行,不像实例资源打满。”
- “慢 SQL 是多了,但还不像根因。”
- “Hikari pending 上来了,要不要先扩连接池?”
- “是不是缓存 miss,把回源打高了?”
这些话都不算离谱。问题在于,如果你还是按“固定几屏面板各看各的”来组织判断,现场会很快变成每个人都能从一张图里找到支持自己猜测的证据,但没人能说明白:
这条等待链,最早是从哪里开始积起来的?
我后来把这类文章越写越窄,最后发现真正有用的不是再讲一版“值班时固定看哪几屏”,而是沿一类真实事故往下走:告警怎么起,最先看见什么,为什么会误判,面板在中途到底帮你排掉了什么,最后又是怎么收敛到可执行动作的。
下面这次事故,不是某次线上复盘的逐字记录,但它足够接近我见过的那类典型夜间等待链现场。
一、告警刚起的时候,最危险的不是信息少,而是每个信息都像原因
那次是凌晨后的写链路抖动。
先响的是订单相关接口 RT 告警,随后超时率开始抬,错误率还没完全炸开。第一眼看上去,很像常见的“数据库慢了,应用跟着排队”。
如果只摘几条当时最容易被看到的数据,大概会是这样:
- 订单写接口 P99 明显拉高
- 数据库调用 RT 上升
- 连接池 pending 开始持续增长
- 慢 SQL 数量比平时多
- 锁等待也有一些抬头
这类现场最容易把人带进一个错觉:
既然数据库相关指标几乎都在涨,那就从数据库本身开始怀疑。
但我现在碰到这种情况,第一步反而不会立刻扎进数据库内部面板。原因不是数据库不重要,而是如果连影响面都没先看清,后面所有判断都容易把“局部异常”讲成“整库事故”。
我先看的,是服务影响面那块最朴素的图:
- 哪些接口在抖
- 是读写一起抖,还是只有一段链路先坏
- P95、P99 是局部尖刺,还是整体都被拉高
- 请求量本身有没有突变
这一步很快给了我一个重要结论:
受影响的主要是写链路,读接口整体还算稳。
这意味着,至少当时还不像“数据库整库性能一起掉下去”。如果是整库资源问题、实例级拥塞,读写通常不会分得这么开。
这个结论本身不能告诉我根因,但它先帮我挡住了第一层误判:
现在看到的数据库抖动,未必是数据库整体先坏;它也可能只是某段写事务先卡住,然后把等待往后推。
二、现场最容易做错的动作,就是把结果当原因
影响面看完以后,我第二步才去看应用侧等待。
因为数据库等待链里,一个特别常见的误判就是:
- 看到线程池排队,就说应用扛不住了
- 看到连接池 pending 上升,就说连接池太小了
- 看到数据库 RT 增高,就说数据库先慢了
但这些量,很多时候都只是同一条等待链传导到不同位置后的表现。
那次我把应用侧几条时间线摆到一起看,重点只有几项:
- 连接池 active / pending
- 获取连接耗时
- 请求线程池 active / queue
真正有价值的不是某个绝对值,而是先后顺序。
那晚的顺序很清楚:
- 连接池 pending 先起来
- 获取连接耗时随后变长
- 线程池排队再往后出现
这个顺序一下子排掉了一个很容易在群里被喊出来的动作:
先扩线程池,或者先把应用实例数拉起来。
因为如果线程池排队是更后面的现象,它更像是请求卡在数据库连接归还这一步之后,业务线程被拖住了,而不是应用计算本身先扛不住。
同样,看到 pending 上来,也不能直接得出“把连接池调大就好”。
如果连接迟迟归还不了,你只是把连接池扩大,很多时候等于让更多请求一起挤到已经堵住的数据库等待链上,现场只会更乱。
到这一步,我能确认的是:
- 应用确实在等数据库这一段
- 线程池拥堵更像后果,不像起点
- 单纯从应用层扩容,大概率不是先手止血动作
但还有一个关键问题没回答:
数据库这段,到底是执行变慢了,还是等待变长了?
三、很多“数据库慢”,其实不是慢在执行,而是慢在等
这是我现在看数据库内部时最在意的一刀。
因为太多现场会直接跳到慢 SQL TopN,然后开始讨论 explain、索引、执行计划,好像只要数据库 RT 上来,就一定是 SQL 自己跑慢了。
可真实的等待链事故里,应用感知到的“数据库慢”,往往混在一起:
- 等连接
- 等锁
- 等事务释放资源
- 真正执行 SQL
- 返回结果
如果不先拆开这个问题,后面的排查方向会很容易歪。
所以那次进数据库相关面板后,我没先盯慢 SQL 排名,而是先看:
- 活跃事务和长事务有没有抬头
- 锁等待时间是不是比慢 SQL 更早变化
- 热点对象是不是集中在少数记录或少数表上
- 数据库内部执行时间,和应用侧感知到的调用时间,差距有没有突然拉大
这里面最关键的发现有两个。
第一,长事务和锁等待抬头的时间,比慢 SQL TopN 明显更早。
第二,慢 SQL 虽然变多了,但很多 SQL 本身的执行计划并没有突然变坏,它们只是排在等待后面,看起来一起“慢了”。
这一下,现场就从“是不是哪条 SQL 退化了”收窄成了另一个方向:
更像是某类写事务把热点记录占住了,后面的请求不是跑得慢,而是排着队过不去。
这是数据库等待链事故里特别容易看反的一层。
因为慢 SQL 面板太显眼,值班时又天然会盯 TopN,所以人很容易把“排队后变慢的 SQL”误认成“主动制造堵塞的 SQL”。
如果这时候开始大聊索引优化、执行计划,讨论当然也不是完全没价值,但它很可能不是这次现场最先该做的事。
四、缓存和回源面板在这里的作用,不是讲体系建设,而是排除另一条很像的故事
到这一步,方向已经收了不少,但我还要再排掉另一类很常见的假设:
是不是缓存先失效,回源把数据库写链路和连接池一起打高了?
因为这类事故长得也很像:
- 请求一多,数据库调用 RT 变差
- 连接池紧张
- 应用线程被拖住
- 数据库里也能看到不少“慢”
如果不把这条线排掉,你还是不能放心把注意力放回锁和事务。
所以我才会去看缓存命中率、miss QPS、关键接口回源量、重试量这些图。
注意,这里面板的角色已经不是文章主角了。它们只是在回答一个很具体的问题:
现在是不是有外部流量放大因素,在把数据库等待继续往上推?
那晚看下来,几个结论都比较干脆:
- Redis 命中率没有出现断崖式下滑
- miss QPS 有轻微波动,但不够解释整段等待链
- 关键写接口的回源量没有异常放大
- 重试量也还没到把系统自己放大的程度
这等于把“缓存先坏,数据库被动吃流量”这条故事先排掉了。
当一条可能性被排掉以后,面板的价值就体现出来了:
它不是告诉你世界的全貌,而是帮你少走一段弯路。
五、真正收敛的瞬间,往往不是看见了新指标,而是终于敢把别的解释放下
影响面、应用等待、数据库内部等待、缓存回源这些线索摆到一起之后,那次现场其实已经能说出一条比较连贯的因果线了:
- 先出问题的是订单写链路,不是整库读写一起崩
- 应用侧最早出现的是连接池等待,不是线程池本身先扛不住
- 数据库内部更早抬头的是长事务和锁等待,不是执行计划突然恶化
- 缓存和回源没有明显异常,外部放大量不成立
剩下的方向就很集中:
去找那段把写事务拖长、把热点记录卡住的东西。
最后顺着事务和锁对象往下查,定位到的是夜间批任务和订单写链路在一段热点数据上撞在了一起。
它不是那种“数据库资源炸满”的事故,也不是“缓存雪崩”的事故,更不是“某条 SQL 突然没走索引”的事故。
更准确地说,它是一次很典型的等待链事故:
- 某类写事务持锁时间变长
- 后续写请求开始排队
- 连接归还变慢
- 连接池 pending 上升
- 业务线程逐渐被拖住
- 应用 RT 和超时一起变差
真正值钱的动作,也不是当场把几十张图看得更细,而是很快做两个收敛动作:
- 先把夜间批任务错峰,别继续和在线写流量硬撞
- 再回头拆那段热点更新,把持锁时间和竞争范围压下去
前者是止血,后者才是治理。
如果那晚一路盯着慢 SQL TopN 走,最后很可能会把事故讨论成一次索引优化会;如果一路盯着连接池,又可能把动作带成“先扩容再说”。
这两件事都不一定完全错,但都不是当时最先该做的。
六、我现在怎么理解“值班面板”这件事
所以我后来越来越少讲“固定几屏面板怎么排”。
不是这些面板不重要,而是如果文章的主结构变成“第一屏看什么,第二屏看什么,第三屏放什么组件”,读者最后记住的通常只是一个排版方案,或者一套值班入口清单。
可数据库等待链真正难的,从来不是图不够全,而是现场太容易被带偏。
更实用的理解应该是:
- 影响面板,帮你先分清是局部链路还是系统性事故
- 应用等待面板,帮你确认应用是在制造问题,还是在承受问题
- 数据库内部面板,帮你分清慢在执行还是慢在等待
- 缓存和回源面板,帮你排掉“数据库只是被外部放大拖高”的可能
也就是说,面板存在的意义,不是为了组成一套“标准值班分屏方法论”,而是为了在事故现场一层层排掉误判。
当你这样用它们时,图就不再是主角了。
真正的主角,是那条因果线:
哪一块先卡住,等待怎么传下去,哪些现象只是后果,哪里才是最值得先动手的地方。
七、如果只记一句话,我希望是这个
数据库等待链事故最怕的,不是没面板,而是每个人都能从面板里看到自己想看的东西。
所以比“多看几张图”更重要的,其实是让自己沿着事故往下问:
- 最早坏的是影响面,还是局部链路?
- 应用是在放大问题,还是在承受问题?
- 数据库是执行慢了,还是等待变长了?
- 有没有别的流量放大因素,在伪装成数据库故障?
这些问题答清以后,面板自然会变成证据。
而不是文章本身。