凌晨两点手机警报像催命一样响起来我眯着眼摸到手机屏幕上跳着“MySQL主库连接数超阈值”。脑子瞬间清醒翻身起床开电脑一顿操作猛如虎连上去一看连接数确实突破了阈值但再仔细一查——好嘛是监控脚本自己连的库没有释放加上业务凌晨的定时任务刚好跑起来两个因素叠一块儿把连接数顶过了线。DB本身稳如老狗QPS、慢查询、锁等待一个异常都没有。把监控脚本的连接池改小问题消失我却损失了一夜睡眠。这种事儿干多了谁都会忍不住骂一句DB监控告警的道理我全明白指标、阈值、通知、降噪说起来头头是道但真到落地的时候不是误报刷屏没人看就是真出故障了告警没响最后背锅的还是自己。“老明白了但就是搞不好”不是不聪明而是没人告诉你那些文档里不写的坑。这个系列我就准备把这些坑一个个扒开从最常挨骂的监控告警聊起把我自己踩过的、看别人踩过的、以及最后怎么绕过去的经验都摆出来希望能让正在被告警折磨的你少走点弯路。1. 告警为什么总挨骂先搞清楚你踩的是哪个坑1.1 不是不懂是没把“告警”当产品设计很多人一听到“告警设计”就觉得虚觉得告警嘛不就是设个阈值、超过就发消息吗但实际做下来你会发现告警系统跟业务系统一样需要从需求、场景、用户、体验四个维度去设计。需求是业务方和DBA真正关心什么场景是故障发生时谁需要知道什么信息用户是值班工程师、研发负责人还是老板体验则是这条告警发出来之后收到的人能不能一眼看懂、能不能直接行动。我见过太多团队把监控告警做成了“数据搬运工”写几个SQL查一下performance_schema或者pg_stat_database然后拿固定值跟指标比超过就Alertmanager发到钉钉/企微群。这不能算错但离“好用”差距极大。真正的告警设计应该像做产品一样先定义清楚“这个告警是给谁看的、解决什么问题、触发后期望他做什么”。比如MySQL主从延迟告警发给DBA和发给研发措辞和附带信息完全不同。DBA需要知道延迟秒数、复制线程状态、最近 relay log 情况研发只需要知道“哪个业务的读流量可能受影响应用侧要不要降级”。做不好告警的团队通常都是把这两类人丢到同一个群里发同一条没头没尾的消息。你去看那些告警做得好的团队他们的告警规则文档长得跟产品需求文档一样每条规则都有负责人、触发条件、影响范围、处理预案、恢复通知。我后来才意识到之前老挨骂不是不会配Alertmanager的route而是压根没把告警当一个正经系统来设计。这个认知的转变比学任何工具都重要。1.2 常见挨骂场景对照你以为的监控 vs 实际的监控我总结了几种典型的“挨骂现场”几乎每个团队都经历过。第一种叫“狼来了”告警天天响但大部分是误报时间长了真出问题也没人看。第二种叫“后知后觉”业务方先发现页面打不开了跑过来问你数据库是不是挂了你这才上去看监控发现慢查询已经堆成山。第三种叫“互相甩锅”告警里信息太少只说“CPU使用率90%”研发问是哪个业务的实例你答不上来对方一句“你监控怎么做的”怼得你哑口无言。这三种现场的本质都不是监控工具不行而是“监控指标”与“用户感知”之间缺了一座桥。你以为监控了数据库的CPU、内存、连接数、QPS、慢查询就是监控了数据库。但业务方感知到的“数据库挂了”是“接口超时”“页面打不开”“订单提交失败”这两者之间的映射关系如果没人去维护告警自然就成了摆设。所以后来我改了一版监控看板把业务侧的接口错误率、平均延时和DB侧的指标放在同一个时间轴上。业务告警和DB告警一起看谁依赖谁、谁引发谁一目了然。这么做之后研发同事对我的态度明显好了很多。监控告警不是只给自己看的仪表盘而是跨团队协作的沟通工具这句话我现在越来越认同。2. 监控做不好的根子指标、阈值、通知这三层全断了2.1 指标层你以为的DB指标和实际业务差着十万八千里先聊聊指标选型。很多人配置监控项习惯照着网上的文档或者云厂商的默认模板来MySQL就监控QPS、连接数、InnoDB缓冲池命中率、主从延迟PostgreSQL就监控事务数、死锁数、缓存命中率。这些指标不能说错但如果你问一句“这些指标反映业务什么状态”好多人都答不上来。举个例子连接数高一定代表数据库有问题吗不一定。我遇到过应用侧连接池配置了最大值200但业务高峰期并发一上来连接池被打满应用报“无法获取连接”数据库端的连接数看起来只是正常偏高但业务已经卡死了。这时候你监控连接数的意义就不大真正需要监控的是应用层“活跃连接数占连接池比例”以及“获取连接耗时”。这属于应用监控但DB监控要是不懂这些就会在排查时把方向搞反。监控指标必须从业务链路里“长”出来。我现在的习惯是接一个新的数据库实例之前先画一张简单的调用链路客户端-负载均衡-应用-连接池-DB。然后逐个节点想这个环节要是慢了DB层面会有什么表现应用层面会有什么表现哪个指标能最先感知到比如订单系统用户下单写库核心链路是“插入订单表扣减库存”。那监控重点就应该是这两个表的行数变化、锁等待时间、binlog写入量而不是泛泛地看整个实例的TPS。只有指标和业务场景咬合在一起告警才有意义。2.2 阈值层静态阈值是原罪指标选得再准阈值设得不对也是白搭。最常见的翻车姿势是拍脑袋定阈值CPU超过80%告警、内存超过90%告警、连接数超过500告警。看似没毛病实则漏洞百出。因为不同数据库、不同业务、不同时间段指标的“正常范围”完全不一样。核心交易库CPU常年80%跑着没事一个内部报表库CPU飘到40%就可能出问题。你用一个固定阈值去套所有实例结果就是误报和漏报并存。静态阈值的另一个问题是没考虑时间维度。很多指标有典型的周期性比如电商业务白天高、凌晨低工作日的波峰和周末完全不是一回事。如果阈值只在“全天任何时间超过X就告警”那白天可能高频误报凌晨真出问题反而被淹没。我踩过最惨的坑是给一个批处理库设了“活跃会话30”的告警平时凌晨2点跑批活跃会话能冲到80因为天天报值班的人直接忽略了这个规则。后来有一天跑批卡死活跃会话卡在200群里的告警飘了半小时没人处理直到业务方打电话来。解决思路是让阈值跟着时间走。最简单的做法是按小时设置不同的阈值比如业务高峰期连接数阈值设300低谷期设150。更聪明一点的是引入基线告警用过去14天的历史数据预测当前时刻的合理范围超过基线一定比例才触发。Prometheus 机器学习或者一些商业APM工具都支持这个能力开源方案也有类似的东西。这里要提醒一句基线告警刚上线时误报特别多因为模型还没学到业务的突发模式。建议先观察一段时间把基线的敏感度调到一个既能发现异常、又不至于天天吵人的程度再逐步放开。2.3 通知层告警疲劳与告警轰炸前面两层做得再完善通知层不懂“降噪”照样挨骂。告警疲劳这个词各位肯定不陌生当一个群里每天刷几百条告警人就会自动选择无视大脑会把它们过滤成背景噪音。等真正的故障告警出现时没人注意到值班群变成了“比谁眼神好”的游戏。告警轰炸常见有三种来源。第一是重复告警同一个问题每5分钟发一次一小时12条全是同样的话。第二是抖动告警指标在阈值边界来回横跳触发-恢复-再触发把人的情绪一起搞崩。第三是风暴告警一个根因导致多个指标异常于是CPU、内存、连接数、锁等待、慢查询一起报一个故障产生几十条告警真正有用的根因信息被淹没。要治理告诉疲劳我的经验是三层filter第一层在产生端去重。同一实例同一指标在未恢复前只发一次后续所有变化都走“update”而不是“create”。第二层加“持续时长”条件。比如CPU90%持续5分钟再告警这能过滤掉大量瞬时抖动。很多监控系统都能配“for”参数但真正用的人不多。我在团队里立了个规矩没有“for”条件的告警规则一律不许上线宁可延迟一两分钟也要保证告警是“值得响”的。第三层聚合和分级。告警不能一视同仁要分P0/P1/P2P0直接电话短信P1发钉钉/企微并值班人P2汇总成日报。Alertmanager里的route inhibit group_wait就是干这个的配置对了告警量能下降70%还不漏真故障。3. 一套能少挨骂的DB监控告警落地参考3.1 指标选型从业务视角倒推监控项我自己沉淀了一套指标选型的方法不一定适用所有团队但至少能让你少走弯路。第一步先把数据库实例按角色和业务重要性分成几类核心交易库、普通业务库、报表分析库、中间件库比如Redis、ES背后的数据源。不同类别的库监控密度和告警阈值都应该不一样。核心交易库每5秒采集一次P0告警普通业务库每30秒采集一次P1告警报表库甚至可以不配实时告警只配看板。第二步对每个分类用“黄金信号”框架来选指标。数据库的黄金信号我总结为可用性能不能连上、进程在不在、容量磁盘、内存、连接数是否接近上限、延迟SQL响应时间、事务执行时间、错误死锁、锁等待超时、复制中断。这四个维度每个维度挑1-2个最核心的指标就够了不要贪多。比如MySQL核心库我最终保留的监控项是这些可用性实例存活TCP连通SELECT 1、主从复制状态Seconds_Behind_Master容量磁盘使用率、连接数使用率当前连接数/max_connections延迟平均查询耗时按业务维度拆、慢查询数1s错误锁等待事件次数、死锁次数、复制线程停止状态每个指标都要回答一个问题“它异常时业务会怎么样”答不出来的指标直接删掉。这套筛选下来一个MySQL实例的监控项一般控制在15个以内告警规则5-8条已经能覆盖90%的故障场景。3.2 阈值动态化用基线和趋势代替固定值配置阈值的时候我强烈建议优先用“趋势”和“基线”来替代绝对值。绝对值的问题是业务增长和资源规划一变老阈值立刻失效。比如半年前连接数200就算高半年后业务涨了连接数常年400你再守着200的阈值那真是天天告警。趋势告警的做法是对你关心的指标做一次和过去24小时/7天同时间段的对比。如果当前值显著超过历史同期比如超过3倍说明出了异常。这种告警的优点是能自动适应业务增长和周期性波动缺点是配置起来比固定阈值复杂。但我说句实话这世界上没有既简单又准确的告警你不想被骂就得在配置上多花点心思。具体到实现我之前用Prometheus PromQL 里的deriv()和predict_linear()做过简单预测。比如对于磁盘用量可以用过去6小时的斜率预测未来4小时会不会打满会的话就提前告警。这比单纯设置一个“磁盘90%”的阈值要智能得多。对于需要基线的地方我习惯维护一个“业务高峰日历”把双11、大促、月末结账这种特殊日期标出来当天自动把阈值上调一定比例。3.3 告警降噪聚合、去重、分级、路由降噪是告警系统里最能直接体现“经验”的部分。我见过不少团队Alertmanager配置了但只会用最基础的group_by: [alertname]导致下游还是被轰炸。我的降噪配方分四步第一步去重。同一实例同一告警规则在状态恢复之前只发送一次。后续状态变化走“已持续X分钟”的更新消息但不再新增一个独立的告警。这一步能减少40%的条数。第二步聚合。把“同一个根因”触发的所有告警合并成一条。比如数据库连接池耗尽可能同时触发连接数告警、活跃事务告警、应用获取连接超时告警。在Alertmanager里用group_by: [cluster, instance]按实例聚合再把常见的关联告警做inhibit规则比如“活跃事务告警发生时抑制同一实例的慢查询告警”因为这大概率是一个根因引起的一系列症状。第三步分级。我参照Google的SRE实践把告警分成三类Page立即通知值班人要求5分钟内响应、Ticket发到工单系统当天处理、Log记到日报里每周复盘。分级要由DBA团队和研发团队一起定不能只让监控管理员拍脑袋。定级标准是“业务受影响程度”而不是“指标偏离程度”。比如慢查询多但DB响应还在业务容忍范围内那就只是Ticket如果慢查询导致核心接口P99超过200ms了才算Page。第四步路由。不同告警要发给不同的人。核心库的Page告警打值班DBA电话同时研发接口负责人非核心库的告警进群就行不要电话骚扰。路由配置在Alertmanager的route节点里做规则要尽量细化宁可多写几行也要把“谁负责什么”分清楚。3.4 告警自愈能自动处理的别打扰人自愈是降噪的进阶版也是“不挨骂”的杀手锏。很多告警其实是可以自动处理的但如果你不写自愈脚本就只能靠人肉看。我做过一个典型例子MySQL复制中断。以前复制线程停了监控发现后发告警值班人上来先登录实例看错误日志然后重新start slave还要再验证Seconds_Behind_Master慢慢追上来。流程跑完至少10分钟如果发生在凌晨值班人第二天都是黑眼圈。后来我写了一个自愈脚本监听复制中断的告警webhook自动执行以下步骤先检查错误码如果是常见的1236binlog被清理、1062主键冲突跳过这类可预期错误直接按预案处理——跳过事务、重新同步位点、恢复复制。处理完了自动发一条通知“已自动恢复耗时XX秒错误原因XXX”。如果是不认识的错误码才转人工Page。这样一个季度下来复制中断告警的处理时长从平均20分钟降到了2分钟值班同事终于能在凌晨睡个整觉。自愈不是万能药但凡是那些“操作固定、风险可控”的告警都应该尽量自动化。我自己在团队里推了一个规矩一条告警规则上线时必须同时写清楚“如果触发谁来处理、手动操作步骤是什么”。如果这个操作可以在脚本里描述清楚那就直接做成自愈。做不到的再走Page。4. 实操中踩过的坑和排查实录4.1 典型案例连接数告警刷屏最后发现是连接池配置问题有一阵子我们某个业务库每天晚上固定时间连接数飙升告警刷屏。我一开始怀疑是慢查询拖住了连接上去一看慢查询不多但活跃会话也没异常。后来把时间点对齐才发现每天晚上21:00有个定时任务会批量从应用拉取数据而应用侧的连接池配置是maxTotal500空闲连接回收时间又设得特别长。深夜批量任务一跑应用短时间内申请大量连接但连接池不会马上归还导致DB端连接数堆积。这个问题的根结不在DB而在于连接池参数。排查完之后我们做了三件事把连接池的最大空闲时间调短加了testWhileIdle给定时任务单独设了一个小的连接池实例DB侧连接数告警加了一个“持续5分钟”的条件。从此这个告警再也没半夜响过。复盘的时候我们意识到很多DB告警其实是应用行为不当的信号单纯在DB侧加宽阈值是治标不治本。连接数告警响了第一反应应该是看“是谁发起的连接”而不是急着提DB的max_connections。4.2 典型案例慢查询告警天天有但业务根本不卡另一个经典翻车案例是慢查询告警。我们有个库每天慢查询数超过1000条的告警几乎持续触发但业务方反馈毫无感知。我一开始也困惑后来把慢查询日志捞出来看发现90%的慢查询都是同一类后台统计SQL扫描全表几百万行单条执行2秒。但这SQL跑在只读从库上而且只在空闲时段执行从库本身压力不大业务读请求都是走另一套缓存所以用户根本感觉不到。这个案例逼我反思“慢查询”到底该怎么定义单条SQL执行超过1秒就算慢但对一个离线统计来说2秒完全能接受。后来我调整了策略按SQL类型分开监控线上核心链路的SQL执行超过200ms才告警后台分析类的SQL以执行计划是否变化、是否索引失效为准。告警规则细化之后这个库的慢查询告警量从每天几十条降到了一周两三条而且每一条都是真正需要看的。从这里我总结出一个排查技巧任何告警触发后先不要立刻去看DB状态先看“这条告警对应的业务活动是什么”。从业务事件倒推找到“为什么这个时候这个指标会异常”比盯着指标本身更容易定位根因。4.3 排查技巧快速定位是监控问题还是DB问题当一条看起来不合理的告警出现时我自己按下面这个顺序排查基本能在10分钟内确定是DB真有问题还是监控数据搞鬼。第一步查数据链路。告警里的指标值是从哪采集的是Prometheus直接抓的还是通过exporter转了一层exporter本身有没有异常我遇到过exporter因为权限问题拿不到processlist返回空数据导致“活跃连接数”被统计成0反而触发“实例不可用”告警的事。所以看到诡异的指标先怀疑采集端。第二步交叉验证。用另一套独立的工具去查同一个指标。比如Prometheus说连接数800就用命令行直接登录MySQL执行show processlist;看看实际进程数是不是差不多。如果两个数据对不上通常监控侧的问题不要急着动DB。第三步看时间相关性。把DB告警和业务请求量、应用日志错误叠在同一个时间轴上。如果告警时间和业务波峰吻合指标本身没有异常增长那就是阈值设太紧了如果告警时间和某个应用发版时间吻合十有八九是应用改动带来的行为变化去查那个发版的代码。这套流程走下来基本不会漏判。最怕的是看到告警就紧张直接去重启数据库——重启一时爽排查火葬场故障原因没找到下一次告警还会来。5. 从“老挨骂”到“被认可”的几个习惯5.1 告警规则要写变更记录告警规则不是配好就不动了业务在变数据量在变实例数量在变。如果谁改了告警配置都不说等下次告警爆出来后人根本不知道这条规则当初为什么这么设。我给团队定了一个规矩所有告警规则的增删改都必须走一个轻量级的变更评审至少要在群里一下相关人员说明“改了什么、为什么改、期望达到什么效果”。这个习惯救过我一次。有一次磁盘告警阈值从80%调到90%结果没过多久磁盘真的涨到85%但因为有调整记录大家知道这是临时放宽等运维扩充磁盘后才恢复正常。如果没有记录这个时间段里磁盘85%没告警万一业务出问题第一个被怀疑的就是“为什么告警没响”。有了变更记录即使最后发现阈值调错了也能快速回溯是谁在什么场景下做的决定。5.2 每周做一次告警复盘我每周五下午雷打不动做一件事拉出这一周所有的告警记录逐条过一遍。哪些是误报、哪些是重复告警、哪些是真正发现问题的。误报的找原因能优化规则就优化不能优化就标注“已知现象无需处理”。真正发现问题的复盘处理过程有没有可以改进的地方比如发现时间是不是太晚、告警信息是不是不够清晰、处理预案是不是没跟上。复盘的好处不是当场能解决多少问题而是逼着大家去思考“这些告警存在的价值”。坚持做一个月你会发现告警量肉眼可见地下降因为重复的规则被合并了低价值的规则被删除了真正重要的规则被加粗了。更重要的是团队里每个人对“什么样的告警值得响”逐渐形成共识以后业务方收到告警也不会直接跑来质问“这又是什么鬼”因为他们知道能发出来的都是过过脑子的。5.3 给业务方一份“告警解释手册”最后一招也是我真心推荐的给经常合作的产品、研发、运维同事写一份通俗版的《数据库告警解释手册》。不要写技术参数就写“当你收到这条告警时意味着什么你可能需要做什么”。比如“连接数过高”这条手册里写数据库当前同时服务的连接数快达到上限了通常是应用连接池配置不当或者某个业务SQL卡住不释放连接。请先检查你的应用日志里有没有获取连接超时的报错如果有请联系DBA提供show processlist截图。这份手册看起来原始但效果奇好。以前业务方一收到告警就问我“数据库是不是挂了”现在他们自己会先对照手册做一个初步判断来问我的时候已经带着应用侧的信息了沟通效率高了一个量级。我始终觉得监控告警不只是技术手段它是一种跨团队的语言。你把告警解释清楚了大家才能用同一种语言协作否则永远是在互相猜疑中挨骂。我个人在实际操作中的体会是DB监控告警这条路没有一劳永逸的配置也没有万能的开箱即用工具它更像是一个持续迭代的过程。你不必一开始就追求完美但一定要在每一次误报和每一次漏报中记下一笔然后逼着自己去调整。今天被骂“监控怎么做的”明天就能拿出“我改了哪些规则、为什么改、带来了什么效果”的底气。这个系列后面我还会继续写一些具体的工具选型和配置示例但如果你能把上面这些思路先吃透那就已经超过大半只会机械配阈值的同行了。
