告警去重与压缩

运维圈有个老梗:「告警不是怕不来,就怕来得太多太猛」。我记得刚带团队那会儿,碰上过一次底层网络抖动,五分钟内涌进来二十多万条重复告警。监控大屏直接卡死,值班同事手机震到没电。从那之后我就明白——去重压缩不是锦上添花,是保命手段。

这一章咱们把重复识别、压缩策略、时间窗口配置这三件事拆开揉碎了讲。说白了就是:怎么判断两条告警是「同一个事」、怎么把一堆重复告警压缩成一条、以及时间窗口设多大才不误杀也不漏报。

1 重复识别:给告警打「指纹」

判断重复最朴素的方式:比较告警的几个关键字段。我习惯把这套规则叫「告警指纹」——它由一组固定字段拼接而成,指纹相同就视为重复。

  • 完全匹配:告警源标识 + 告警类型 + 告警级别 + 发生对象。四个字段一模一样,铁定是重复。
  • 模糊匹配:有些字段(比如实例ID里带IP后缀)允许正则剔除变量部分。例如 /app-server-(\d+)/ 只看前缀 app-server。
  • 相似度计算:针对文本描述字段,用编辑距离或Jaccard系数算相似度。超过阈值(比如0.85)就算重复。这个慎用——算力开销大,实时流处理扛不住。
我的建议:线上环境优先用完全匹配+正则模糊匹配。相似度计算留给离线分析用,别在实时管道里跑Levenshtein。

2 压缩策略:三种常见模式

确认了重复,接下来怎么「压」?我见过不少团队上来就丢告警,结果重要信息一块儿丢了。压缩不是丢弃,是聚合+保留关键信息。下面三种策略我按推荐程度排了个序。

策略 做法 适用场景 我个人踩过的坑
相同告警压缩 指纹完全一致的告警,取第一条生成,后续只更新计数和时间戳 大量重复的CPU/内存/网络抖动告警 忘了带上首次/最近发生时间,排查时不知道持续了多久
相似告警合并 模糊匹配或同源规则合并,生成一条「聚合告警」附带子告警列表 同台机器多个磁盘分区同时IO高 合并后子列表太长,显示时截断反而丢了关键盘符
风暴抑制 设置速率阈值,超过后直接丢弃非关键告警,只输出一条风暴预警 网络闪断/机房掉电导致海量告警 阈值设低了,把恢复告警也丢了,事后复盘缺了恢复时间点
# 以相同告警压缩为例,核心伪代码
def dedup_and_compress(alert):
    fingerprint = make_fingerprint(alert, fields=["source","type","severity","object"])
    window_key = f"win_{fingerprint}"
    # 从时间窗口缓存中取
    entry = cache.get(window_key)
    if entry:
        entry.count += 1
        entry.last_occurred = alert.timestamp
        # 不产生新告警,只更新缓存
        return None
    else:
        cache.set(window_key, AlertEntry(alert, count=1))
        return alert  # 产生一条

3 时间窗口配置:小了误报,大了漏报

去重压缩都离不开一个核心参数——时间窗口。窗口定义「多长时间内的重复算作一波事件」。我最早配置的时候随手填了个3秒,结果大量P0告警被压缩成一条,影响面都看不出来。后来改成60秒,又发现业务抖动恢复后,新一波告警因为还落在旧窗口里被错误压缩成老的。

窗口类型分两种:

  • 固定窗口:整点对齐,每分钟/每5分钟一个桶。实现简单,但会切断连续事件。举个例子:告警发生在12:00:59和12:01:01,明明只差2秒,却被分到了两个窗口。
  • 滑动窗口:以每条告警的真实发生时间为起点向后看一段时长。更自然,但内存开销更大。我一般用滑动窗口+过期淘汰,窗口大小设置成告警平均持续时间的1.5倍。
我曾经犯过的一个错误:给所有告警类型统一设了一个60秒窗口。结果内存告警是分钟级持续、网络告警是秒级抖动,压缩效果天差地别。现在我的做法:按告警类型/级别独立配置窗口。比如P0设120秒,P3设30秒。

还有两个常被忽略的参数:

  • 静默期:一条告警被压缩后,下次再出现相同指纹是否立刻输出?建议加一个最小间隔,比如30秒内即使窗口过期也不重复上报。
  • 最大压缩计数:聚合告警正文里显示的count上限。我见过count显示999999的,毫无意义。设个100,超过后用「100+」表示就够了。
告警去重与压缩流程 原始告警流入 指纹生成(字段匹配) 是否重复? 时间窗口内 压缩 更新 count / 时间戳 生成 输出告警 / 聚合记录 时间窗口:滑动120s / 固定60s 静默期30s | 最大计数限制100 压缩后保留字段: 首次时间 | 最后时间 | 发生次数

4 避坑指南 & 最佳实践

说白了,这套方案跑得好不好全看参数调教。我把自己踩过的坑列出来,希望能帮各位省几周调参的时间。

  • 不要全局用一套窗口。P0持续告警和P3瞬发告警行为完全不同。我习惯给每个告警级别打标签,窗口配置从标签继承。
  • 去重前先做归一化。不同采集器上报的时间格式可能差毫秒级,字符串里的空格、大小写、IP前导零都得统一。不然指纹永远对不上。
  • 压缩告警也要保留原始明细。压缩后的聚合告警里带一个外键,指向原始告警表的ID范围。复盘时能钻取查看每条原始记录。
  • 设置告警压缩占比告警。如果某类告警的压缩比超过99%(比如10000条重复压缩成1条),说明可能基础配置有问题,触发一条「告警压缩异常」的运营告警。
一个小提醒:去重压缩不是万能的。对「同一个根因产生不同表象」的告警(比如主机CPU高引发多个进程告警),压缩解决不了,需要用后续的告警关联分析。所以压缩是第一步,别以为做完压缩就高枕无忧了。

总结一下我自己的配置模板——滑动窗口 + 完全匹配指纹 + 按级别差异化窗口 + 静默期30s + 最大计数200。这套配置在千亿级监控数据量的场景下跑过,压缩率稳定在85%~97%。你可以拿这个当起点,再根据业务调优。