
值班手机凌晨两点半震醒屏幕上弹出一条数据库告警连接数超阈值。你爬起来打开监控页面看到连接数确实飙了一会儿但业务日志和接口都正常。你回一句“观察中暂不处理”继续睡第二天复盘会却被点名为什么误报告警天天有为什么真出故障时告警反而被淹没为什么总是后知后觉说实话DB监控和告警这套东西理论谁都说得出来但真正落地不挨骂难度比想象中大得多。我干运维这几年数据库的监控告警反复重构过三轮踩的坑写出来足够连载一个系列。这是第一篇先把“告警为何搞不好”这件事讲透。1. 告警一身汗复盘一身骂问题到底出在哪先讲个很常见的典型场景。某天下午监控大屏突然变红数据库连接数告警、主从延迟告警、慢查询告警一起往外蹦。值班同事第一时间冲上去看发现连接数是从默认一百跳到三百主从延迟偶发一秒慢查询其实是一条历史遗留SQL被业务重跑了一遍。业务侧全程无感但值班侧折腾了两个小时最后结论是“资源抖动无需处理”。这种场景出现三次以后所有人对告警的信任度都会降到冰点导致真正出事的时候没人当回事。问题根源不在于监控工具不够强也不在于值班人不负责而在于我们普遍把“告警”当成“监控到位”的唯一衡量标准。只要告警数量多、看板图表多就觉得自己监控做得很好。这恰恰是反的告警设计得差数量越多噪声越大真实故障越容易被淹没。1.1 满屏P1等于没有告警我见过最极端的项目一个数据库实例配置了四十多条告警规则从“buffer cache命中率低于90%”到“磁盘剩余空间低于20%”全设成了P1级别通知对象是所有运维和开发。结果如何第一天发出两百多条告警第二天大家开始静音第三周真出主从切换故障时相关告警在群里躺了十几分钟没人响应。这里有个很朴素的道理告警不分级等于所有告警都是最高级而人的注意力和响应意愿是有限的。真正重要的故障被人为淹没在无关紧要的噪声里这就是“满屏P1等于没有告警”的本质。我后来把告警级别收敛成P0/P1/P2/P3四档并且约法三章P0是业务不可用必须电话加群双重轰炸P1是需要人在约定时间内介入排查P2是记录观察交接班处理P3只是信息连通知都不用发。这个看似简陋的约定执行起来比一堆复杂规则管用得多。1.2 三种典型的告警翻车姿势复盘了多个项目之后我把最常见的错误做法归纳成三类。第一类是阈值拍脑袋。很多人设置连接数阈值80%、磁盘空间80%是照着通识文档写的不看业务高峰期和低峰期曲线。结果业务大促时连接数天然高告警响个不停凌晨流量低时磁盘增长慢等真正快满的时候又因为“之前报了没管”而错过处理窗口。阈值必须来自真实历史数据而不是标准模板。第二类是告警全量上报。抓到什么异常就通知所有人却不想想这个动作是否有后续。慢查询超过一秒钟就告警开发收到后没法判断这条SQL是不是核心链路运维收到后也没法直接定位问题来源最后告警变成“阅后即焚”。没有处理动作的告警本质上只是制造焦虑。第三类是指标堆砌。以为监控维度越多越好把数据库所有状态指标都堆上去但没有区分基础设施指标、数据库状态指标和业务体验指标。前两类指标异常不一定会影响业务第三类指标异常才是用户真正能感知到的。这三类混在一起权重一致就等于没有优先级等出真问题时排障链路反而更长。这一章想表达的核心是在动手调监控模板之前先承认告警设计是独立的技术工作而不是监控平台自带的附加功能。有了这个前提下面的原则和实操才有意义。2. 告警不只是“通知”三个基本盘决定你能不能睡好觉我经常跟团队说一句话一条告警从发出到被处理应当是一个完整的决策链路而不是一个简单的“某指标超阈值”的广播。判断一条告警设计得好不好看三个关键维度就够了可解释性、可响应性、可收敛性。这三件事做不到后面多少优化都是治标不治本。2.1 可解释性告警消息必须自带上下文很多告警推送只有一行字“xx数据库慢查询数超过阈值”。收到这种消息值班人员第一反应是打开浏览器登录监控平台找半天才看到具体实例和SQL文本。更离谱的是等终于定位到是哪条SQL时已经是告警发出的二十分钟后。本来一条慢查询告警应该在一分钟内进入判断流程结果大部分时间花在“找到底在说哪个实例”。理想的告警信息至少应该包含这些字段实例标识和所属业务、指标当前值和触发时间窗、同实例最近一段时间的指标走势、如果是SQL相关还需要具体的SQL摘要和执行计划入口、历史同类告警的处理记录。我在公司落地告警通知时把关键信息拼在消息正文里并且附上一个直达Grafana面板的短链接。值班同事收到消息不用开电脑手机上点开就能看到关键图表响应速度明显提升。举个例子一条慢查询告警如果写成“prod-db-mysql-01 慢查询数(当前120基线30) 最近15分钟持续高位疑似SQL_IDabc123平均耗时2.1s点击面板查看执行计划”和原来那种“慢查询突增”对比价值完全不同。告警文案里把上下文补齐是把“告知信息”升级成“辅助决策”的第一步这是所有后续降噪方案的地基。2.2 可响应性每条告警都必须能回答“然后呢”如果一条告警发出来接收人不知道要干什么那就是设计缺陷。最常见的例子是“主从延迟超过5秒”这种告警。收到后你确实知道主从延迟了但然后呢要看大事务要看锁等待要看从库硬件负载还是直接考虑切换如果告警本身不携带这些排障路径建议不同人会有完全不同的处理方式有人选择重启从库有人不处理有人到处问同事处理质量参差不齐。更关键的是告警必须连着一个“动作”这个动作可以是自动化的也可以是人工判断闭环。比如连接数告警触发后自动拉起一个诊断快照采集当前processlist、活跃事务、连接来源IP分布把这些快照随告警一起归档。后续处理时不需要再翻历史判断依据已经准备好了。再比如慢查询告警自动从慢日志里提取TOP N条SQL摘要连同最近一周的执行次数趋势图一并推送。这些自动化手段不复杂但能让接收人快速推进决策。另一个容易忽略的点是告警状态的闭环。很多团队只关心告警触发不关心恢复通知导致一个故障恢复了所有人还在紧张排查。恢复通知必须是告警设计的标配。我在Prometheus的Alertmanager里会为每类告警配置恢复时间窗口和恢复通知模板让流程完整闭环。只有触发没有恢复的告警体系等于只有警报没有解除长期下来团队会形成“反正没人通知恢复那我就当它一直有事”的麻木心态。2.3 可收敛性能发一条绝不发十条这是DB监控告警里最痛的点。一个主库抖动可能同时触发连接数告警、活跃会话告警、慢查询告警、复制延迟告警。每一条单独看都合理但它们都是同一个根因的表象。如果不做收敛值班人员会收到连续十条告警。表面上信息量很大实际上把根因藏在了噪声背后因为人很难在一堆告警中快速归类“这都是同一件事”。告警收敛业界已经有成熟机制核心是分组、聚合、抑制、互斥四件事。分组就是把相同维度下的告警合并到一条通知里聚合是在时间维度上把短时间内重复触发的告警收拢成“连续告警N次”的一条抑制是当高位告警出现时自动静默由它引发的低位衍生告警互斥是避免同一个时间窗口里同类故障反复通知。比如某个主库实例整体不可用后应用层、缓存层、数据库层的关联告警就应当被抑制优先通知最上层的业务可用性告警。可收敛性做到位之后告警从“十条相似但没法一眼概括”变成“一条带聚合信息的告警”排障判断路径大幅缩短。这一条原则和前面的可解释性、可响应性配合起来才真正称得上一套能用的告警体系。3. 降噪实战把告警从“轰炸”改成“精准投递”原理讲清楚之后说说实际操作层面怎么改。这部分不局限于某一个具体的数据库监控工具但会以Prometheus加Alertmanager、以及我实际用过的类夜莺监控平台为例核心思路通用。下面按“指标选择、阈值计算、通知路由、聚合抑制”四条线展开。3.1 先治指标选择哪些数据库指标真正值得进告警很多人第一步就走错了把能采到的指标全配上告警。实际上数据库指标存在明显的分层基础设施层指标CPU、内存、磁盘IO、数据库状态层指标连接数、活跃会话、锁等待、复制延迟、SQL性能层指标慢查询、错误日志必须分层对待。我用过一个很笨但很有效的筛选方法把配置的所有告警规则拉出来逐一问三个问题——这个指标异常是否一定代表业务受影响这个指标异常时团队是否有成熟响应方案这个指标告警会不会和其他告警形成同源重复。三个问题只要有任何一个是否定的就降级或直接下线这条规则。按这个方法筛完我负责的数据库集群告警规则从四十多条降到了十四条覆盖没变弱噪声减少七成。具体来说数据库状态层指标里真正值得优先告警的只有这四类可用性问题实例宕机、主从切换、进程异常退出、容量风险磁盘空间预测将在24小时内耗尽、连接数逼近上限、数据一致性问题主从延迟持续高位、复制中断、关键性能退化核心SQL执行计划变化导致耗时大幅上升。其他指标可以作为看板观察项但不建议直接进告警通知。3.2 阈值别拍脑袋用时间基线取代固定阈值静态阈值最不靠谱的地方在于数据库负载天然存在周期性特征业务工作日的上午十点和凌晨两点的连接数、慢查询数不是一个量级。一个固定阈值如果按高峰期设低峰期出问题报不出来如果按低峰期设高峰期天天误报。可行的做法是引入基线概念。比如连接数阈值不设置成固定值而是“超过过去同一时间段比如最近7天同一小时均值的50%”这样高峰期和低峰期使用同一套规则但实际门槛是动态的。Prometheus里的表达式可以这样表达sum(mysql_global_status_threads_connected) / avg_over_time(sum(mysql_global_status_threads_connected)[7d:1h]) 1.5这个规则表达的意思是当前连接数比过去七天同一小时的均值高出一半才触发告警。对于有明显周期性的指标这类动态基线比固定阈值可靠很多。同理慢查询数量也可以用类似的基线表达式。比如慢查询的“绝对数量”在高并发时段天然多用“当前5分钟慢查询数和过去七天同一时刻均值对比”更能反映真实的异常信号。另一点容易被忽略的是持续时间窗口。一个指标偶尔尖刺几秒钟和持续十五分钟处于异常状态重要程度完全不同。凡是短期抖动不影响业务的指标都应该设置一个“pending时间窗口”比如持续一分钟以上才触发告警。当时我接手的一个环境就是没设这个窗口任何瞬时波动都秒级告警值班同事被折腾得很惨。加上持续时间判断之后误报率直接降了大半。3.3 把通知路由做对谁负责、通知谁、用哪种强度告警路由每个人理解不同但有一个大方向是确定的按“影响范围”和“责任系统”分方向而不是所有告警发给所有人。数据库中间件层例如代理或连接池的告警发给DBA组应用慢SQL的告警发给对应业务线的开发接口人基础设施类告警发给运维值班组。注意这里说的是分组通知不是把所有人都拉到一个群里轰炸。以Alertmanager为例可以用一组简单的route配置实现按标签路由和分级route: group_by: [alertname, instance, db_role] group_wait: 30s group_interval: 2m repeat_interval: 4h routes: - matchers: - severity p0 receiver: phone-call repeat_interval: 30m - matchers: - db_role primary receiver: dba-oncall - matchers: - service payment receiver: payment-dev这里把P0级别的告警单独拎出来走电话通道把主库相关告警定向给DBA把支付业务的告警定向给支付开发组。实际使用时可以根据团队架构把receiver换成对应的企业微信、钉钉或飞书机器人。配置本身不复杂但很多人压根没花时间梳理这一层导致告警全量涌向同一个群。做路由有一个原则每个接收方收到的告警数量应该显著减少但和自身职责相关的告警一条不漏。判断是否成功的标准很简单就是看接收方是否还主动说“这个告警跟我有什么关系”。如果说说明路由还需要再收敛。3.4 用Alertmanager做聚合和抑制的通用配置聚合和抑制是降噪里收益最明显的两个动作。我先说一个自己配置过的、有代表性的Alertmanager配置片段然后解释每个关键字段的作用。核心的聚合配置是保证同一次故障产生的多条告警归为同一条通知。假设一个MySQL实例的活跃会话、连接数、慢查询同时异常三条告警如果分三次推送接收人会被打断三次。但如果把它们按alertname、instance分组通知就会变成“一条通知包含三个告警项”。这就是前面route配置里group_by字段的作用。抑制规则解决的是“同一根因下低维度告警由高维度告警接管”的问题。比如实例宕机严重告警时业务探活失败、连接失败这些衍生告警就没必要再推送了。Alertmanager的inhibit_rules可以这样配置inhibit_rules: - source_matchers: - alertname MySQLInstanceDown target_matchers: - alertname MySQLConnectionFailure equal: [instance]这条规则的含义是当同一个实例同时出现实例宕机和连接失败告警时连接失败告警被抑制。这个机制能极大减少故障时的“告警叠加轰炸”让接收人只看到最上层、最能定位根因的那条。另外还有个实践心得告警恢复通知也要做数据聚合恢复后按分组发一条“xx实例故障已恢复持续时长N分钟”比逐条恢复通知更清爽。这套配置跑了一周后我们的告警通知量从每天百余条降到平均不到二十条而且基本每条都有明确动向。4. 一次真实事故的告警复盘为什么一堆告警反而延误了排障纸上谈兵告一段落讲一个真实事故场景。这次事故发生在某次大促前夜的压测阶段数据库集群确实出了问题但暴露出的最关键问题不是故障本身而是我们的告警体系在真正需要它的时候拉了大垮。完整复盘一遍你应该能理解告警降噪不是锦上添花。4.1 凌晨两点半的告警轰炸现场当时值班手机开始连续震动先是活跃会话数告警然后是慢查询数量告警接着是主从延迟告警最后是新连接失败率告警。前后不到十分钟收到超过十五条告警涉及同一套MySQL主从集群的三个实例。值班同事一度以为发生了主从切换立即登录监控平台去查拓扑结果看着告警列表反而懵了到底先看哪一条这里就是典型的“告警轰炸反噬”。每条告警单独看都成立了活跃会话确实升高慢查询确实变多主从延迟确实存在新连接也确实开始失败。但这些都是同一个根因引发的连带现象如果告警系统当时做了抑制和聚合最合理的告警应该只有一条比如“核心交易库活跃会话数持续五分钟超过500数据库整体性能下降疑似大事务锁竞争”然后附带一份诊断快照。4.2 完整排查链路从第一条告警到根因确认事后我们回溯了当时的完整排查链路你来感受一下时间是怎么被浪费的。第一步值班同事先打开活跃会话页面发现大量会话处于Waiting for table metadata lock状态。此时时间是凌晨2:43。第二步查询processlist找出持锁会话对应的SQL发现是一条DDL语句在等待某张表的元数据锁。此时是2:47。第三步回去查这张表最近有没有长事务发现一个后台批处理事务在事务开启后一直未提交期间对同一张表做了长时间写入。此时是2:52。第四步才真正定位到根因批处理程序因为一个网络超时没有正常提交事务导致事务一直挂着后续DDL被阻塞连接越积越多从库同步也因为来不及执行事务而延迟。整个过程花了大概十一分钟。但为什么说延误了因为这几步操作完全可以预先自动化。如果告警信息的上下文里带上processlist快照、活跃事务列表、锁等待矩阵值班同事收到第一条告警时手机上就能直接看到“持锁会话ID23375事务未提交已超600秒等待的SQL执行计划是……”这样的结论十一分钟的排查时间缩到两分钟以内完全没问题。4.3 如果告警设计得足够好过程可以节省多少用这次的案例复盘我把关键环节的审计时间列了一个表大家可以清楚看到告警上下文带来的收益排查环节原流程耗时优化后的告警上下文辅助定位受影响的实例3分钟告警消息自带实例ID和角色识别等待事件类型5分钟自动附带processlist快照和等待事件分析定位持锁事务4分钟自动附带活跃事务详情表关联业务变更记录3分钟告警面板附带最近变更事件时间线这张表不是理论推演是后来我们把同样的巡检逻辑固化成一个诊断采集脚本后重新模拟验证得到的数据。核心就是告警不应该停留在“指标超了”这个层面而要尽可能把第一轮排查要做的事情在告警触发的同时自动完成。另外还有一个心得告警里的链接一定要好找。出事故时人已经很紧张如果链接需要在一堆菜单里翻找心气先泄了一半。我们后来把所有关键面板的URL做成短链接写进告警模板手机点开就能看到图效率提升非常明显。这一项值不值得做经历一次半夜故障就能理解。5. 别在告警细节里卷了回到业务视角重新设计告警做完降噪和上下文增强之后告警体验已经好了很多。但另一个问题又会浮现出来即使每个数据库指标都设计得合理仍然很难回答一个最核心的问题——业务到底受影响了没有数据库一切指标正常不代表业务没有问题数据库某个指标异常也不代表用户真的有感知。到这个阶段我会建议不要继续在告警规则细节里卷而是往上一层思考整体监控架构。5.1 从“数据库有问题”到“业务受影响了吗”传统DB监控习惯盯资源指标和状态指标这当然有必要但它回答的是“数据库这台机器/这个进程现在如何”而不是“用户的请求现在体验如何”。我见过不少团队数据库层面的指标全部正常但接口P99时延已经翻了两倍用户投诉进来了才发现问题。反过来数据库一个线程抖动指标上显示异常但业务侧毫无影响告警却已经把值班电话打炸。更合理的做法是建立“业务体验指标优先”的视角。对于数据库而言重点观察的应该是这样几类信号核心链路上数据库查询的响应时间分布、单位时间内的失败请求量、数据库返回错误码的速率。这些指标和用户直接相关一旦异常意味着业务真的在劣化。如果这些指标正常即使底层数据库有一些不太严重的资源抖动优先级也应当往后放。这也是为什么我在团队里推动把数据库告警分成两类一类是“业务影响类告警”比如核心接口数据库查询耗时P99超过阈值、交易类SQL失败率升高另一类是“容量风险类告警”比如磁盘预测未来N小时写满、连接数趋近上限。前者要让人马上动起来后者只要进入趋势观察列表按时跟进即可不需要半夜轰炸。5.2 用SLO思维判断告警优先级大多数告警规则是“某个指标在某个时间点超过某个阈值”这种判断是割裂的没法回答“这段时间内整体服务达到目标了吗”。引入SLO思维后告警可以围绕一个时间窗口内的服务目标达成率来设计。比如核心数据库查询延迟不再看“单次超过两秒就告警”而是看“过去一个小时内P99延迟达到目标比如小于500毫秒的时长占比是否低于99%”。那种单个慢查询偶然产生但整体体验正常的情况就不会再触发误报。SLO和告警的结合还有一个直观的好处它能帮你确定告警的优先级。当整个服务健康度已经处于SLO边缘时任何一条相关告警都应该升级处理当服务健康度远离SLO红线时一些低级别告警可以自动延期。这样的优先级判断比单纯按指标类型划分更接近真实情况。实际操作时可以用一个计分机制来合并评估业务核心链路查询延迟达标率、错误率、容量风险指数加权计算出一个服务健康度分数。健康度优良时数据库底层的非关键告警自动并入日报健康度恶化时自动提升所有关联告警的通知级别。这样从“一个指标一个逻辑”的碎片化告警升级为“面向用户体验的告警决策”接告警的手感会完全不同。5.3 日常演练和告警自检清单告警体系不是配置完就结束了它需要持续维护就像一个需要定期体检的人。我建议每个季度做一次告警自检盘点以下几个方面。每一条告警规则在过去一个月触发了几次处理记录是否完整。如果某条告警触发后没有对应处理动作也没有业务影响就删掉。随机抽取几次真实故障的时间线对比告警触发时间和人工感知业务异常的时间如果人工感知比告警早说明告警漏报或者指标没选对。检查通知路由是否有人员变更后残留的问题。团队人员变动最容易被遗忘离职同事还挂在告警通知人里并不少见。做一次故障演练故意制造一次典型的数据库故障比如长时间未提交事务、磁盘空间快速写满观察告警从触发到恢复通知的完整链路是否顺畅。这些动作不复杂但极其重要。告警体系的可靠性不是靠上线那一瞬间保证的而是靠持续的小步维护积累下来的。我自己就有过教训某条处于“下线候选”名单里的告警一直没清理结果在一次演练中被它干扰了判断演练结论都偏了。从那以后告警自检变成固定节奏雷打不动。6. 最后说几句心里话做DB监控告警这些年最深的感悟是技术方案其实没有太多神秘的难点在于克制与取舍。敢于删掉那些“看起来很有用但实际没人处理”的告警敢于在指标不明确时先观察而不通知敢于把排障动作前置到告警内容里。这些决策的背后是持续对抗“多就是好、全就是稳”的本能冲动。如果你现在正被一堆数据库告警折磨不妨从今天开始做一次减法把告警规则清单拉出来按“是否可解释、是否可响应、是否可收敛”过一遍删掉至少三分之一不达标的规则。把省下来的通知量换成告警内容里的排障上下文。这一轮调整做完你会发现值班体验和团队对告警的信任度都会上一个台阶。下一篇系列内容我准备写一写DB监控里的“假指标”识别就是那些看着在动、其实完全不能反映故障的监控项。这类指标往往藏得深危害大跳过它们后续还会踩大坑。如果你也有类似的经历欢迎评论区聊聊你被哪类告警“坑”得最惨。