Debugging Production Issues at 2 AM: A Survival Guide

March 2026 · 20 min read · 4,730 wordsAdvanced

凌晨 2 点调试生产问题:生存指南

凌晨 2:17。PagerDuty。 警报显示支付服务错误率为 95%。847 个受影响交易。你的心猛然一沉。 你立刻清醒过来——那种特有的肾上腺素,只有在生产事故发生时才会涌现。你的手机已经解锁,笔记本电脑正在启动,咖啡机在肌肉记忆的驱动下开始咕噜作响。这是你职业生涯中的第 847 页。没错,你在追踪这个数字。你在第 200 页之后开始计数,当时你意识到这将是你职业生涯的一个决定性部分,如果要在不合时宜的时刻被叫醒,至少你应该对此有数据记录。 支付服务。当然是支付服务。它总是支付服务,或者认证层,或者那一个三年前有人发誓“只是个简单封装”的微服务,现在却已经扩散成你一半基础设施的关键依赖项。 在第二次警报发给你的经理之前,你可能只有 90 秒。也许在客户开始涌入支持渠道之前只有三分钟。再过五分钟就会有高级管理人员醒来,开始在 Slack 上询问问题。时间在悄然流逝,你的手在移动,而你的大脑已经开始回忆你在几百起这些事件中建立的心理检查清单。 这不是你第一次骑马。但没有人告诉你凌晨 2 点的生产调试是什么样的:它永远不会变得更容易。你只是变得更快。你构建更好的工具。你犯更少的愚蠢错误。你学会在质疑一切的同时信任自己的直觉。你培养出一种第六感,能分辨到底是什么真正出了问题,而不是看起来出问题的东西。而最重要的是,你学会明白,一个 10 分钟的事件和一个 4 小时的事件之间的区别往往取决于你回应的前 60 秒。 本指南是我希望在第一页之前有人告诉我的一切。

头 60 秒:如同你的工作存亡依赖于此进行分类

任何生产事件的第一分钟都是纯粹的混乱管理。你的大脑仍在启动,你可能还没穿裤子,而你需要做出关键决定,以决定这个事件是在几分钟内解决还是几个小时。 以下是我每次都遵循的步骤: 立即确认警报。 不要等着“先调查”。确认它。这会停止升级链,并向你的团队发出有人的讯号。我见过太多事件,因为第一位响应者想要“先看一下”才确认,导致多人被叫起。那 30 秒的调查让你失去了本可以继续睡觉的备份响应人员。 打开你的事件响应仪表盘。 不是应用。不是日志。是你的仪表盘。那个能让你一眼了解系统健康状况的仪表盘。对我来说,那是一个自定义的 Grafana 面板,显示错误率、延迟百分位、数据库连接池、队列深度和所有关键服务的 CPU/内存。我可以在不到 5 秒的时间里看到影响范围。 检查问题是否仍在发生。 这听起来显而易见,但我曾被叫醒处理那些在警报发出前 30 秒就自行解决的问题。监控系统可能存在延迟。警报阈值有评估窗口。有时问题已经解决,在你开始撤回部署或重启服务之前,你需要知道这一点。 评估客户影响。 不是理论上的影响——而是真正的影响。现在有多少用户受到影响?是 100% 的流量还是 5% 的流量?是局限于一个地区、一个客户群体,还是一个功能?这决定了你的响应紧迫性以及你是否需要叫醒更多的人。 在这个特定事件中——凌晨 2:17 的支付服务——我的仪表盘在 8 秒内告诉我我需要知道的一切。错误率:94.7%。受影响请求:在过去 5 分钟内 847 个。地理分布:全球。客户群体:所有。支付提供者 API 延迟:正常。数据库连接:正常。问题不在上游或下游。是我们的问题。 那时我知道,我将迎来一个漫长的夜晚。

在压力下真的有效的调试方法论

每个人在冷静、喝着咖啡并在下午 2 点的开发环境中时都有调试方法论。很少有人有能在你半昏睡、CEO 在事件频道中并且每一秒停机都在损失真实金钱的情况下依然有效的方法论。 我使用一种我称为“影响范围到根本原因”的方法。这并不复杂,但在你大脑仅以 60% 的容量运转时,这种方法有效。 从影响范围开始,而不是根本原因。 这很违反直觉。每个本能都会告诉你要立即找到根本原因。抵制这种本能。首先,准确了解是什么坏了,是什么没坏。绘制故障的边界。这有两个目的:它阻止你在正常系统中追逐无用信息,并且往往通过排除法直接指向根本原因。 对于支付服务事件,我花了 90 秒绘制影响范围: - 支付发起:失败 - 支付状态检查:失败 - 支付 Webhook:失败 - 退款处理:正常 - 管理支付查询:正常 这个模式告诉我一些重要的事情:读操作正常,写操作失败。这是一个数据库问题、队列问题或权限问题。三种可能性而不是三十种。 跟随数据流,而不是代码流。 当你午夜调试时,你没有时间逐步跟踪代码。跟随数据。支付请求从哪里进入系统?接下来去了哪里?在哪里失败?我调出了我们的分布式追踪(谢天谢地,我们有这个),看着一个请求在系统中流动。它通过了认证、通过了速率限制、通过了验证,但一尝试写入数据库时就挂掉了。 数据库。就在那。 首先检查无聊的东西。 磁盘空间。内存。连接池。文件描述符。证书过期。DNS。无聊的东西比任何聪明的错误都更容易导致生产系统崩溃。我曾在凌晨 2 点被叫醒,因为某人的 cron 作业填满了磁盘。我被叫醒,因为证书过期。我被叫醒,因为有人改变了 DNS TTL,却不等传播完成。 在这个案例中,数据库连接池达到了 100%。每个连接都在使用。那么为什么?流量并没有激增。查询模式也没有改变。有什么东西保持连接处于打开状态。 信任你的监控,但要验证一切。 监控系统会说谎。并不是恶意——它们只是软件,而软件也有bug。我见过监控系统报告健康的服务,而实际上这些服务完全宕机。我见过它们报告不存在的错误。始终手动验证关键路径。对于支付系统,我准备了一张测试信用卡和一个 curl 命令。我可以在 10 秒内验证整个支付流程。 我进行了测试支付。它挂了 30 秒并超时。监控没有说谎。我们真的宕机了。

让我了解数据库连接的一切的事件

让我告诉你第 312 页的故事。那是三月的一个星期二,凌晨 3:47,它改变了我对数据库连接管理的看法。 我们正在进行一次快速促销。流量很高但并不算前所未有——我们处理过更大的流量峰值。然后突然,每个接触数据库的服务都开始超时。连接池耗尽。典型症状。显而易见的答案是:扩大连接池的大小。 于是我们这样做了。我们把它翻倍。然后我们把它三倍化。但问题变得更糟。 我意识到,有时解决方案会使问题更糟。我们添加到池中的每个连接都是另一个试图在已经超载的数据库上执行查询的连接。我们在 DDoS 自己。 实际问题是什么?一名开发者添加了一个新特性,对一个包含 5000 万行的表进行全表扫描。没有索引。查询花费了 45 秒才能完成。每个命中该代码路径的请求都需持有数据库连接 45 秒。流量足够时,我们耗尽了连接池,并不是因为连接不够,而是因为每个连接都被卡在那个可怕的查询上,等着完成。 解决方案不是增加连接数,而是结束那个查询、添加索引,以及在应用层实施查询超时。 这个事件教会了我三件事: 连接池耗尽是一个症状,而不是疾病。 当你看到它时,不要立即扩大池。问问为什么连接没有被释放。查询慢吗?是否存在死锁?有东西在保持事务开放吗?连接池告诉你另外有问题存在。 查询超时应该无处不在。 每个数据库查询应该设置超时。每个 HTTP 请求应该设置超时。每个队列操作也应该设置超时。超时不是可选的。它们是服务降级与完全关闭服务之间的区别。当出现问题时,超时让你能快速失败,而不是积累被阻塞的连接,直到整个系统崩溃。 监控连接池使用率是不够的。 你需要监控连接的生命周期。平均连接保持的时间是多长?百分位数 P99 是多少?如果你的平均连接生命周期突然从 50 毫秒跳到 5 秒,即使你的连接池还没有耗尽,你也存在问题。这是你的早期警报系统。 回到凌晨 2:17 的支付服务事件。我检查了连接生命周期。平均:8 秒。P99:
C

Written by the Cod-AI Team

Our editorial team specializes in software development and programming. We research, test, and write in-depth guides to help you work smarter with the right tools.

Share This Article

Twitter LinkedIn Reddit HN

Related Tools

Base64 Encode & Decode — Free Online Tool COD-AI vs Cursor vs GitHub Copilot — AI Code Tool Comparison SQL Formatter & Beautifier — Free Online Tool

Related Articles

10 Online Developer Tools That Save Hours Every Week — cod-ai.com CSS Beautifier vs Minifier: When to Use Which Code Review Checklist: What I Look for After 10 Years of PRs \u2014 COD-AI.com

Put this into practice

Try Our Free Tools →