先把话说在前面这篇文章不是教你怎么装监控系统也不是让你背几十个Linux命令。它要解决的是我在做运维和性能优化时最头疼的一个问题——当线上出故障时你凭什么说它“不正常”。没建立过性能基线的团队遇到CPU飙高只能靠猜遇到内存泄漏只能靠重启遇到半夜告警只能靠“先看看”。而有了基线之后异常识别就变成了一道有参照物的判断题这个指标超过了历史水位多少是瞬时抖动还是持续漂移是业务流量本来就涨了还是系统真的出了问题这篇文章我会从基线建设的选型、采集、存储、量化到异常识别的判定规则和真实排查案例把整个闭环完整走一遍。内容不挑发行版CentOS、Ubuntu、openEuler都能用适合正在做Linux性能优化、或者准备给团队搭一套轻量性能观测体系的同学参考。1. 决定建基线之前先把“基线”这件事说透很多团队不是没有监控而是监控数据攒了一堆却从来没有被认真“用”过。讨论性能基线之前我们得先确认自己说的“基线”到底是个什么东西否则极容易做成一个跑几天就没人看的僵尸任务。1.1 没有基线的时候性能问题是怎么变成甩锅大会的我见过太多类似的场景某天下午业务方反馈“系统变慢了”运维看了一眼top发现CPU使用率85%于是判断“负载太高”建议扩容。扩容之后业务确实不卡了但第二天同样的时间点又慢了再扩容……循环往复没有人能回答“85%到底算不算高”。问题就出在85%这个数字本身没有意义它需要一个参照系。如果过去30天同一时段CPU平均值是30%那85%就是异常如果过去30天同一时段CPU本来就在80%~90%之间波动那85%就是正常水位真正的问题可能在别处。没有基线你的每次判断都是在“裸奔”。性能优化最反直觉的一点是你无法优化一个你没有度量过的系统更无法度量一个你没有历史的系统。1.2 基线的本质给系统拍一张带时空坐标的X光片基线的本质不是一张静态的“健康证”而是一个多维度的动态参照系。它至少包含三个维度时间维度一天内不同时段的指标水位不同比如业务系统白天高、凌晨低批处理系统恰好相反。基线和异常识别必须考虑“同一时段对比”而不是拿凌晨3点的数据和下午3点的数据比。空间维度不同节点、不同实例之间的指标分布用于发现“某台机器掉队”的局部异常。版本维度代码发布、配置变更、扩容缩容都会让指标发生结构性变化。基线必须能区分“因为变更导致的正常平移”和“因为性能劣化导致的异常抬升”。打个比方基线就像你给身体做年度体检之后留下的完整报告。单项指标正常与否取决于体检报告上的参考范围而参考范围正是基于大量健康人群统计出来的。性能基线就是给服务器做的“体检参考范围”。1.3 为什么不建议直接拿监控系统默认告警当基线我见过很多团队直接把Prometheus/Grafana的默认阈值搬过来当基线CPU 80%告警、内存80%告警、磁盘90%告警。这套默认值最大的问题是它完全不认识你的业务。同样是CPU 80%对于一个每秒扛几万QPS的网关来说是家常便饭对于一个平时只有个位数流量的管理后台就是灾难。默认阈值是给“从零开始”的人兜底用的它不叫基线叫“初筛规则”。真正要做基线应该从这两个问题出发这个系统的核心指标在过去一段时间里的“正常形态”长什么样哪些指标一发生变化就意味着我该紧张了带着这两个问题去选指标、定采集策略文章后面所有动作才有意义。2. 指标选型与采集工具组合我到底该盯哪些数这可能是最容易被忽视但又最重要的环节。很多文章一上来就让你装node_exporter、配Grafana、搞告警规则结果数据面板一堆图表真出问题的时候还是不知道先看哪个。我自己的经验是基线指标宁缺毋滥但核心维度一个不能少。2.1 七类必采指标从CPU到TCP重传按层梳理我在搭建基线时把系统指标分成七层每一层对应一组“典型问题”。你可以直接拿这个清单当采集对照表层次核心指标主要判断的问题CPUuser、sys、iowait、steal、load average是否计算密集、是否有锁竞争、是否被虚拟机/宿主机争抢内存total、available、used、page cache、swap使用量是否存在内存泄漏、缓存回收是否正常磁盘IOutil、await、svctm、iops、队列长度存储是否成为瓶颈、是否存在随机小IO风暴网络带宽、并发连接数、TCP重传率、TCP timewait是否存在连接泄漏、带宽是否打满、链路质量文件描述符fd总数、fd使用率是否存在句柄泄漏进程/线程运行队列长度、上下文切换次数、线程数是否存在线程风暴、调度延迟应用/中间件请求延迟P99、错误率、活跃连接数系统指标正常时应用层是否仍然劣化这套指标有个好处覆盖了从硬件到操作系统再到应用的全链路。任何一次性能劣化都能在其中至少一层留下线索。比如Java应用频繁Full GC你从系统层看到的是CPU user升高、内存available波动、但load可能没有想象中的高这时候光看系统指标不够还要把GC日志作为辅助基线。所以建议在应用层至少留一个“可采集的P99延迟”指标哪怕只是简单的HTTP接口耗时统计。2.2 工具组合sysstat系 tsar 自建定时器怎么搭配指标定好了接下来是“怎么采”的问题。工具不需要多但每个工具必须明确它的职责边界。我目前的标配组合是sysstat系工具sar、mpstat、pidstat、iostat这是Linux性能基线的基石。sar负责周期性地把CPU、内存、IO、网络等数据写入/var/log/sa/天然自带历史留存能力相当于操作系统的“黑匣子”。mpstat和pidstat则用于故障时刻的定向深挖它们不是采集主力而是定位利器。tsar阿里开源的采集工具最大的优点是“开箱即用”。它会把几十项指标统一都采下来同时支持输出到本地文件或远端监控系统。如果你的团队还没有正式监控平台用tsar做基线数据的快速积累非常方便。自建采集脚本针对应用层指标比如请求延迟、错误率、业务QPS这些指标sysstat和tsar都采不到需要自己写一个小脚本定时写入日志或监控库。注意脚本本身要轻量别为了采集性能数据反而把性能搞坏了。这里有个关键建议采集工具一旦选定就不要频繁更换。换工具的代价不只是重新部署而是基线数据会出现断档。sysstat的sa文件是按月轮转的tsar的时序数据如果中间停采几天那几天的基线就空白了后续做异常识别时那一段就是盲区。2.3 采样粒度和保留策略不是采集得越密越好采样粒度看似越细越好但我建议你克制一点。每5秒采一次CPU数据持续一个月会产生大量数据点但绝大多数是冗余的。性能基线关注的是“形态”和“趋势”不是“每一个瞬间”。我自己在实践中的经验值是系统级指标每60秒采样一次已经足够覆盖绝大多数问题。对于特别重要的生产集群可以提升到每30秒但不要低于30秒否则采集进程自身的开销会开始变得明显。应用层指标每10秒到30秒采样一次这个取决于你统计延迟的方式。如果是聚合统计可以放宽如果是逐请求采样则需要更细。保留策略原始数据至少保留30天按天聚合的数据保留6个月以上按月聚合的数据建议保留1年以上。原因很简单——你想发现“去年同期同样的大促流量下系统表现如何”没有长期数据就无从谈起。提示关于swap指标的采样不要只看已用空间要同时关注swap in/out的速率。很多系统swap空间占用挺高但长期不换页这其实是正常的真正异常的是swap in/out速率陡增说明内存已经吃紧到开始频繁换页了。3. 数据怎么留存才不白采一份可落地的基线落地流程指标选了工具装了数据也会自动往sa目录里写了。但这时候离“基线可用”还差好几步。很多人就是倒在这一步数据攒了几百MB最后却不知道该怎么变成“参考范围”。3.1 基线窗口要多长才够用4周是最低消费如果只采集一周的数据你的基线大概率是偏的。原因很简单一个业务系统往往有周期性规律周一的流量曲线和周日完全不一样月初的批处理和月底的结账逻辑不一样。我的建议是基线窗口至少要覆盖一个完整的业务周期。大多数业务系统以周为周期所以4周是够用的最低消费如果是月度周期性明显的业务比如财务系统建议直接拉满8周。4周还有一个附带的好处能在数据里覆盖至少一次版本发布、一次流量高峰和几次小故障。这些事件本身就是基线的“参照物”有了它们你才知道指标在什么情况下会发生什么样的波动。3.2 给数据打业务标签版本、容量、流量变化必须记录这是我在实际工作中吃过亏之后总结出来的教训。有一段时间我拿到基线数据做异常识别发现某台机器的CPU比历史同时段高出一截当时差一点就要告警。后来一查发布记录才发现前两天刚上线了一个新版本逻辑中多了一层加解密操作。CPU升高是预期的不是故障。从那次之后我养成了一个习惯每发生一次变更都要在基线数据上进行标注。具体做法可以很简单不需要引入多复杂的元数据中心就是用一张文档表记录“时间范围 变更内容 涉及节点”或者更自动化一点在时序数据库里写入一条带注释的Annotation。需要打标签的事件至少包括这四类代码发布、配置变更、内核参数调整扩容、缩容、流量切换大促、压测、重点活动中间件版本升级、JVM参数调整否则你后面做异常识别时会经常遇到“数据看起来异常但其实事出有因”的窘境。基线数据如果没有业务上下文价值会大打折扣。3.3 量化基线的统计口径均值是陷阱百分比位才是朋友有了一堆历史数据之后怎么把它浓缩成一个“参考区间”这是基线建设中最核心的一步。我的建议是用P50/P95/P99 低频统计量来刻画一个指标的正常形态而不是简单地算平均值。举个例子某个接口的响应时间在一天内可能出现这种情况99%的请求耗时都在100ms以内但每过几分钟就会有一个慢请求耗时3秒。平均下来可能显示150ms看起来挺正常但如果你看P99就会发现它是300ms而P99.9已经飙到了2.8秒。这就是均值陷阱。做性能基线和异常识别一定要分清自己关心的是“大多数情况”还是“极端情况”P50中位数描述大多数请求的体验适合观察整体水位。P95/P99描述尾部体验适合捕捉毛刺和长尾问题。Max/Min描述极端情况只用于观察是否有严重异常不作为常态判断依据。比如CPU使用率我习惯这样量化基线过去4周内、同一星期、同一小时段取P50作为正常水位P95作为容忍上限超过P95连续N分钟判定为异常。这种“同一时段 百分比位”的方式能天然避开业务周期性的影响比简单地拿全天均值做阈值科学得多。4. 异常识别方法论哪种“波动”真的需要你半夜爬起来基线建好之后真正的重头戏才开始异常识别。但“异常”这个词需要拆开看。不是所有偏离基线的变化都值得告警也不是所有故障都会表现为明显的指标飙升。异常识别是在做判断题判断错了比不判断更糟糕——假警报会消耗团队的信任而漏报则会让基线沦为摆设。4.1 区分三类异常瞬时尖峰、持续漂移、结构性突变我在实践中把异常分成三类不同类型的异常要用不同的策略去应对第一类瞬时尖峰。持续几十秒到几分钟的指标突刺然后迅速回落。这类异常通常不会造成长期影响但如果是高延迟尖峰则可能有隐蔽问题比如GC停顿、定时任务抢占、网络抖动。识别策略是“短窗口 高阈值”一般设置成超过基线P95的2倍才告警避免频繁打扰。第二类持续漂移。指标不是突然飙高而是每天比前一天涨一点点一两个星期之后才到达危险水位。这是最容易被忽略的异常因为它每天都在“正常”地变化。典型场景是内存泄漏、连接数缓慢增长、临时文件积累。识别策略是基于趋势而不是基于阈值的——计算最近7天的日均值与前28天基线的偏差偏差持续扩大就要处理。第三类结构性突变。指标在某个时间点之后整体抬升或下降并保持在新水平运行。这往往是变更导致的“新常态”比如版本发布后CPU上涨10点。它不是故障但需要确认“这个新常态是否符合预期”。识别策略是变点检测最简单的方式就是对比变更前后的同期数据是否存在显著均值差异。4.2 判定规则怎么设计3σ、环比阈值和复合条件设计判定规则时我推荐从简到繁不要一上来就上机器学习。实际经验是一套“3σ 环比 复合条件”的规则已经可以覆盖90%以上的场景。3σ法则是最经典的做法假设指标服从正态分布超过均值加3倍标准差的值视为异常。但这个法则有个前提——Linux性能指标很多时候并不服从正态分布而是有明显的周期性、偏态和重尾。所以我更喜欢“分位数区间法”把过去N周同一时段的数据排序取P5到P95作为正常区间超出区间的视为异常。分位数方法对偏态分布更稳健不需要假设数据分布。环比阈值是补充手段当前时刻的指标值与上一时刻的差值超过某个比例就触发告警。比如CPU在5秒内从10%跳到60%即使绝对数值没有超过基线P95这个跳变速度本身也值得关注。环比阈值适合捕捉“突然启动的耗CPU进程”这类场景。复合条件是避免误报的关键。任何一个单独的指标触发条件都不能直接告警最好满足多个条件才触发。比如CPU使用率超过基线P95同时运行队列长度大于CPU核心数状态持续超过3分钟三个条件同时满足再告警误报率会大幅下降。为什么要“持续超过3分钟”因为瞬时毛刺可能只是某个进程的临时抖动人为看一眼就会回落。持续3分钟以上才说明系统真的“卡”在这个状态里了。4.3 一个常见误判案例磁盘util 90%其实完全是假警报这里分享一个我印象很深的误判案例也帮你理解为什么不能只看单指标。有一回监控显示某台数据库服务器的iostat磁盘util持续90%以上值班同事差点半夜拉起来扩容。我让他先别急着动去做两件事第一看iostat -x里的await和svctm第二看这段时间有没有批量任务在跑。结果发现await只有2毫秒svctm是1.2毫秒。这意味着磁盘响应非常快只是请求排队紧密。再查业务原来正在跑一个夜间数据导出任务它用顺序读的方式把大量数据扫了一遍。顺序读场景下磁盘队列基本一直是满的util自然会到90%以上但这完全不叫瓶颈。这个案例的核心教训是异常识别不能脱离指标之间的关联性。util高需要看awaitCPU高需要看是user还是iowait内存紧张需要看swap飙没飙。单项指标触发只是线索关联指标确认才是结论。5. 从基线到定位一次线上CPU softlockup的完整排查复盘说完了理论我来复盘一次真实的排查过程。这次排查如果没有前期建立的基线完全不可能在10分钟内锁定问题。它正好能把这些方法串起来。5.1 现象基线图上出现连续三天同一时段sys异常某天上午连续第三天收到同一类告警某台KVM宿主机在早晨7点50分左右%sysCPU从基线的12%飙到接近70%持续约8分钟然后回落。第一天我以为是偶发第二天依然出现到第三天我意识到这不是巧合。因为已经有基线我很清楚这是从一周前才开始出现的新规律。于是第一步就排除了“正常业务波动”。我把目光锁定在7点50分这个时间点哪个任务会在这个时间固定出现我先用crond的日志过滤了一遍计划任务没有发现异常。计划任务排除了就开始怀疑是不是系统层面有什么周期性的内核活动。因为%sys高通常意味着系统调用和内核路径繁忙。5.2 收缩范围用pidstat和perf找出真凶进程为了第二天能捕获现场我在次日早晨7点45分加了两个命令让它们持续跑到8点# 每2秒记录一次进程级CPU占用重点观察%sys pidstat -u -d -t 2 pidstat.log 21 # 用perf记录内核调用栈定位sys时间都花在哪 perf record -F 99 -a -g -- sleep 300结果很快就浮出水面。pidstat日志里一个名为kworker的内核工作线程从7点50分开始CPU占用飙到300%以上多核累计对应的函数栈在perf report里直指mpt3sasRAID控制器驱动。这一下全通了这台宿主机本来是凌晨的空闲期磁盘灯一直闪烁但我没有单独采过磁盘IO流量。顺着排查思路追下去查明是监控Agent每24小时对全盘文件做一次mmap扫描扫描时产生的元数据写放大触发了RAID控制器驱动的某种较慢路径导致内核线程陷入高占用。5.3 根因与落地对策基线告警定位的联动闭环根因是监控Agent的定时全盘扫描配置过于激进。对策也很简单把扫描时间从早晨7点50分错峰到凌晨3点并调整nice优先级。改动之后连续观察一周同一时段%sys始终保持在基线范围之内。这次排查的完整链路是这样的基线的日常视图捕捉到了“新规律”——连续三天同时段异常这是单靠瞬时告警无法提供的。同一时段对比帮我区分了“周期活动”与“新品异常”因为基线上标注了一周前的全盘扫描没有这么高的CPU。进程级抓包用pidstat和perf精确定位到了内核线程和驱动层把问题收敛到了RAID驱动而不是业务代码。事后我做了一件事在采集指标里增加了虚拟化层和存储控制器层的补充指标并且给这个Agent扫描任务打上了变更标签下次再出现类似现象基线会自动提示“这是一个已知任务变化导致的新常态需要重新评估正常范围而不是当成故障处理”。6. 做基线期间最容易踩的坑以及我的落地清单文章的最后一部分我梳理一下做性能基线期间我自己踩过、也看别人踩过的坑。这些坑如果你不提前规避很可能在跑了三天之后突然发现数据白采了。6.1 踩过的坑采集脚本时区、cron静默失败、数据被日志轮转盖掉坑一采集脚本的时区问题。这是非常常见又隐蔽的坑。二进制包安装的sysstat会读取系统时区但用cron跑自建采集脚本时脚本里的date输出的可能是UTC时间。如果你没有统一后续做“同一时段对比”时就会发现曲线的波峰波谷全部错位8小时。建议所有采集脚本里明文指定export TZAsia/Shanghai date %F %T这样无论服务器时区配置怎么变采集到的数据时间戳都是一致的。坑二cron静默失败。服务器重启后crond服务如果没起来或者脚本依赖的某个路径不存在了计划任务会静默跳过不会给你发任何通知。等你两周后去查基线数据发现中间空了一周只能重采。应对办法是给采集任务加一个“心跳文件”每次执行都更新时间戳然后用一个外部探针检查这个时间戳是否停留在“N分钟之前”。超过阈值就告警说明采集链路已经断了。坑三数据被日志轮转盖掉。如果你把数据存到/var/log/目录默认的logrotate配置可能会在某个时间点把采集数据压缩甚至删除。处理方式是给性能数据目录单独加一条logrotate规则或者干脆把数据放到独立目录比如/data/metrics/避免和系统日志混在一起。6.2 一份可直接抄的checklist如果你现在准备在服务器上建立基线直接照着这个清单做可以少走很多弯路确认需要纳入基线的机器名单首批控制在10台以内优先选核心业务节点为每台机器统一配置sysstat10分钟级自动落盘和tsar分钟级汇总版本保持一致写一个cron任务采集应用层P99延迟、错误率并输出到独立目录为所有采集任务加上心跳检查确保数据不静默中断规划数据保留策略原始数据30天、日聚合6个月、月聚合1年以上用一个表格维护“变更事件流水账”每次发布都要登记积累满4周数据后用分位数法生成第一版基线区间制定告警规则时坚持“多指标复合”避免单指标一触发就半夜打扰每季度做一次基线复核主动找出指标漂移明显的节点而不是等到故障才想起来6.3 个人体会基线建设是“一开始麻烦、后面越来越省事”的事如果你从零开始做这套事情前两周会觉得很繁琐装工具、写脚本、打标签、盯数据。但坚持到一个月之后你会慢慢感觉到自己对这个系统的掌控力不一样了。我现在的习惯是新系统上线第一天就立刻开始采集哪怕还没时间做任何分析先让数据跑起来。因为基线数据的价值是时间的函数晚一天开始就永远少了那一天的历史。而性能优化的很多判断恰恰需要这些“之前正常的时候”的数据当参照物。建立基线的最终目的不是让你变得“更会告警”而是让你在深夜被叫起来的时候手里有一套历史数据撑腰能冷静地判断这个指标异常是真的、持续的还是虚假的问题大概在哪个层面然后直接带着数据去排查。对我来说这种“心里有底”的状态才是做性能基线真正的价值所在。