尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

搞懂TP99、QPS与TPS:从平均时延误区到性能监控实战

搞懂TP99、QPS与TPS:从平均时延误区到性能监控实战 先讲一次真实经历。某个核心接口线上突然变卡用户反馈“下单基本一直在转圈”。我打开监控第一件事就是看QPS结果没涨反而跌了一点再看平均响应时间132毫秒跟平时125毫秒差不多完全看不出异常。直到把目光移到TP999上才发现这个值已经从前一天的1200毫秒飙到了6300毫秒整整翻了5倍。这一眼就锁定了排查方向——不是流量洪峰而是某个线程池或外部依赖出了问题。这就是TP50、TP99、TP999、QPS、TPS这套指标的价值。平时看监控平台满屏都是曲线如果不理解每个指标到底在表达什么很容易被平均值骗过去以为一切正常实际上用户体验已经在崩塌。这篇文章我会从概念到实操把这几个指标讲透包括它们怎么算、怎么采集、告警阈值怎么定、排查问题时怎么看组合适合后端开发、运维、SRE以及刚开始接触性能监控的测试同学参考。1. 这组指标到底在回答什么问题1.1 先从QPS和TPS说起一个是入口流量一个是内部事务QPS的全称是Queries Per Second每秒查询数。很多业务系统把它等同于每秒请求数其实也没错它衡量的是“入口处每秒来多少请求”。TPS的全称是Transactions Per Second每秒事务数更强调一个完整的业务操作比如一个订单从创建到最终落库涉及到多条SQL、多个状态变更这一整套对外表现为一个事务。我举个典型的支付场景。支付网关的QPS可能是3000因为每秒有3000个请求打进来但底层数据库的TPS可能只有800。为什么不相等因为每一个支付请求要查账户、锁余额、调账务接口、写流水一个请求在系统内部会拆成好几个子事务。反过来说如果一个系统QPS很低但TPS很高说明一个请求进来内部做了大量写操作这种系统通常更吃数据库。很多压测报告会把QPS和TPS混着用这点我建议项目里明确区分口径统一了后续做容量评估才不打架。另外说一句你在某些链上浏览器看到的TPS和这里讲的TPS不是一回事区块链那个更偏“每秒打包的交易笔数”跟业务后端监控完全是两套语义别被热搜词带偏。1.2 TP50代表一半用户的真实体感TP是Percentile的工程化表达全称是Top Percentile或Tail Percentile意思就是百分位值。TP50表示50%请求的响应时间在这个值以内换句话说如果TP50是80毫秒那么一半的请求比80毫秒快一半比80毫秒慢。这个指标回答的问题是大多数用户感受怎么样它不是平均数不看极端峰值而是把一段时间内所有请求的耗时排个序找到中间位置的那一个。因为用户的实际体验更像“我这次的请求快不快”不是“所有人平均下来快不快”所以百分位天然比平均值贴近真实感受。我之前接过一个查询服务平均响应时间一直显示60毫秒看起来相当健康。后来单独看了TP50发现已经到300毫秒了。原因是有个低频批量任务周期性跑耗时长强行把平均值拉下来。如果把平均值当健康标准这个服务早就该被优化了。1.3 TP99和TP999给长尾请求准备的放大镜TP99表示99%的请求耗时在指定值以内只有1%的请求会超过这个值。TP999则更苛刻要求99.9%的请求达标也就是每1000个请求里最多允许1个请求超时。越往后越能暴露系统最脆弱的地方。还是用数字说话。某接口一分钟内10个请求耗时为10ms、12ms、12ms、15ms、20ms、20ms、25ms、40ms、200ms、2000ms。平均值是多少236.4ms看起来可接受。但TP90是200msTP999如果按1000个样本近似取最大的那个值可能是2000ms甚至更高。也就是说如果有用户恰好碰到那几个慢请求他的体验就是“打开页面要等两秒”而平均值根本没有体现。TP值本质上是给长尾请求照X光。TP50正常只能说明系统大体能干活TP99和TP999一旦飙升说明某些环节在特定条件下会出现严重劣化。常见的元凶就是GC停顿、线程池排队、数据库连接池打满、外部RPC超时重试这类问题。2. 为什么监控和压测都盯着TP99而不是平均时延2.1 平均数是最容易骗人的统计量平均数的问题在于它会被极端值严重影响。一个请求耗时5秒能顶替250个正常请求的贡献。如果1000个请求里只有1个请求慢到5秒另999个都是20毫秒平均下来大约25毫秒表面看还挺漂亮。但如果你作为用户你就是那个5秒的请求你会觉得这个系统很垃圾。线上业务一般要求SLA常见说法是“TP99小于200毫秒”。这比“平均响应时间小于200毫秒”严格得多因为它直接限制了慢请求的比例。平均时延哪怕达标也不代表大部分请求快TP99达标才敢说绝大多数用户没受罪。我见过有些刚起步的团队只统计“平均耗时”出了几次线上投诉之后才把指标改成百分位。转折点通常是领导问一句话“为什么平均耗时没问题用户一直说卡”这时候你打开监控发现TP999已经红到发紫。踩过这个坑之后我再也不把平均时延作为核心健康指标了。2.2 TP值之间拉开差距往往意味着瓶颈位置不同只看一个TP值是不够的。很多时候我会对比TP50、TP99、TP999三者的相对关系能看到系统当前的瓶颈模式TP50正常TP99略高说明大多数请求快偶发慢请求可能是GC或资源竞争。TP99正常TP999很高说明极端情况下有严重尾巴通常是超时重试、线程池满了以后新请求排队等待。TP50和TP99同时上涨TP999没怎么动说明整个服务普遍变慢更可能接近容量上限所有请求都被拖累。TP50高但TP99不高不太常见如果出现大概率是有两类调用混在一起一类稳定慢一类稳定快需要拆分维度。这套判断方法我实际用了很久。有一次线上反馈偶发卡顿TP50只有30msTP99是120msTP999却跳到2600ms。我顺着慢请求追踪发现是某个下游服务在错误码返回时会占用连接10秒不释放刚好触发重试机制就出现一条极其扎眼的尾巴。2.3 把Littles Law用起来QPS、耗时和并发的关系这几项指标从来不该孤立看。后端系统里有一条非常实用的公式并发数 QPS × 平均响应时间。这个关系叫Littles Law(利特尔法则)在稳定状态下一定成立。举个例子。某服务QPS是1000平均响应时间是200ms那么在任意时刻系统里同时在处理的请求大约是1000 × 0.2 200个。如果你给核心线程池只配了150个线程那么必然有50个请求在排队排队会推高TP99。这时候你看QPS还能维持1000其实线程池快要被打爆了。这个公式还能推导出一个反常识的结论当响应时间上涨一倍QPS如果不降并发数一定涨一倍如果线程池有上限最后必然有大量请求排队等待甚至超时。反过来QPS下降不一定代表压力变小可能是慢请求把线程占满了新请求根本进不来。所以排查问题时如果只看到QPS下跌就以为安全了往往会被表象骗过去。3. 实际工程里怎么算TP值和落地监控3.1 算法层面精确排序和近似估算如果你自己写统计逻辑最直接的方法就是把窗口内所有请求的耗时存下来从大到小排序然后取第99%位置的值。比如窗口内有1万个请求按耗时升序排列第9900个请求的耗时就是TP99。问题在于高并发系统每秒钟可能就是几千上万个请求你在内存里为每个请求保留一条记录成本很高尤其面对长周期统计时内存肯定兜不住。生产环境一般用两类方案一是用HdrHistogram这类高性能数据结构它用直方图桶的方式近似统计能控制内存占用二是用监控系统自带的分位数能力比如Prometheus的Histogram类型配合histogram_quantile函数计算。如果只是事后分析比如从Nginx访问日志里拉一段时间、求TP99用命令行临时算一下也很快。假设日志里每行的最后一个字段是请求耗时单位毫秒可以这样处理awk {print $NF} access.log | sort -n | awk {arr[NR]$1} END {print arr[int(NR*0.99)]}这段命令的逻辑是先抽出耗时字段按数值升序排序再用总行数乘以0.99得到目标位置输出该位置的耗时就是这一段日志里的TP99。胜在简单适合排查问题时快速验证缺点是全量排序慢不适合长时间段的大日志日常量级够用。3.2 监控窗口怎么选10秒和1分钟的区别很大TP值跟统计窗口密切相关。同一个接口用1分钟窗口算TP99和用10秒窗口算TP99结果可能差异很大。1分钟窗口会把瞬时毛刺平均掉曲线看起来平稳10秒窗口更容易捕捉到抖动的尖峰但也会带来更多的告警噪声。我个人的实践建议是“长短结合”。告警和线上排查时用短窗口10秒或者30秒保证能及时抓住问题周报和趋势分析用长窗口5分钟或1分钟级别看整体水位变化。如果只看分钟级平均值像GC导致的几百毫秒毛刺会被旁边的正常请求稀释你根本看不到故障瞬间系统发生了什么。Prometheus里有一类很常见的写法比如计算过去5分钟内HTTP请求耗时的99分位histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))这里需要注意rate和sum的维度要处理干净le这个标签必须保留否则histogram_quantile算出来会完全没意义。刚开始用Prometheus分位数的人十有八九都栽在这个标签丢失的坑上。3.3 采集埋点应该放在哪一层监控指标采集在哪一层决定了它反映的是谁的体验。常见的埋点包括网关层、应用层和数据库层角度各不相同网关层(Nginx/API Gateway)的QPS和TP值反映入口整体情况能看到外部用户的真实体感但难以定位内部到底是哪个服务拖慢的。应用层的指标最有价值因为可以按接口、按调用方、按上游拆开看排查时能快速锁定具体链路。数据库层的TPS和慢查询日志代表数据存储的健康度如果应用层TP99上涨的同时数据库TPS飙高大概率是SQL或锁出了问题。我一般建议至少覆盖两层网关层保留一份长期趋势应用层每个核心接口单独埋点。如果公司监控资源有限优先保证应用层因为排查问题时效最高。4. 告警阈值怎么定才能不被指标骗到4.1 阈值不是拍脑袋拍出来的是跑出来的很多团队第一次设告警直接写“TP99大于300ms就报警”结果一天到晚被吵醒全是误报。问题在于不清楚当前系统的真实水位。没有基线任何阈值都是纸上谈兵。更稳的做法是分几步走。先让系统稳定运行1到2周把这期间每小时的TP99、TP999、QPS都存下来作为基线。然后看正常运行时TP99的波动范围比如通常在80ms到120ms之间那告警阈值可以设在基线的2倍左右200ms到250ms起步。这样既能反映明显劣化又不会因为正常业务波动频繁告警。还有一点容易被忽略核心接口和非核心接口要分开设阈值。查询订单列表允许TP99在200ms但支付接口可能要求TP99小于100ms。把不同重要程度的接口混用一套阈值等于没设阈值。4.2 多指标联动比单指标告警可靠得多只看TP99一个指标很容易出现“凌晨两点收到接口慢的告警爬起来一看是某个大数据任务在抢资源”这种无效操作。后来我学到的经验是把相关指标组合起来判断告警会少一半以上。我常用的一组联动逻辑是当TP99超过基线的2倍同时QPS没有明显变化这时候优先怀疑逻辑或依赖问题当QPS上涨幅度超过50%同时TP99也跟着涨大概率是容量问题考虑扩容或限流当QPS下降但TP99上升先不要慌看是不是线程池被打满、请求在排队。这里每一种组合都指向不同的处置动作。阈值不要设一层就算完。我建议设置两层预警级别TP99超过基线的1.5倍持续1分钟通知到当班同学不强制处理。告警级别TP99超过基线的2倍持续5分钟同时TP999超过基线的3倍需要立即介入。加上持续时间条件能过滤掉绝大多数的瞬时抖动。我见过太多只设阈值不设持续时间的规则最后大家都把告警关了真正出大问题时反而没人看到。4.3 利用Apdex作为补充指标或许更直观TP99适合技术人员看但如果你要给非技术同事解释服务健康度其实Apdex更直观。Apdex全称是Application Performance Index把响应时间分成了满意、可容忍、失望三档。假设你定义满意阈值T为200ms那么小于等于T的算满意小于等于4T的算可容忍超过4T的算失望。Apdex得分等于(满意请求数 可容忍请求数 / 2)除以总请求数得分越高说明体验越好1.0表示所有请求都满意。这个指标的好处是能把一组耗时归一化成0到1之间的数方便做趋势对比。但它无法看出长尾也不能替代TP99去做异常定位只能作为管理视角的补充。5. 常见问题与排查技巧实录5.1 QPS没涨、TP99却飙高先查有没有线程在排队这是线上最经典的一类问题。系统没有明显的流量增长用户却在抱怨变慢打开监控发现QPS纹丝不动TP99已经从80ms涨到200ms。这种时候线程池的排队情况往往是关键。正常场景下请求到了线程池直接拿线程执行响应时间约等于代码执行时间。当某个下游依赖变慢、连接池被打满或者锁竞争严重时线程拿不到执行权新请求只能在队列里排队。排队时间会计入响应时间因此TP99立刻上升但入口QPS不会变因为请求仍然是正常到达的。如果排队时间超过客户端的超时设置就会表现为请求失败或超时重试。处理这个问题我一般会先看线程池活跃线程数和队列大小。如果活跃线程接近最大值队列还在堆积基本可以断定在线程池这一层然后用全链路追踪找到具体阻塞在哪个外部调用上。很多Java应用通过Actuator或Arthas都能实时看到线程池状态排查起来并不复杂。5.2 TP99没涨、TP999涨得离谱多半是重试机制搞的鬼TP999异常而TP99正常说明99%的用户体感还好但每1000个请求里总有几个体验极差。这种场景非常隐蔽尤其是当故障请求占比不大时平均值和TP99都被掩盖了。我查过一次很典型的案例。某个服务通过RPC调用下游下游偶尔超时上游设置了超时后自动重试一次。正常情况下一次成功调用耗时100ms超时却要等3000ms重试又得等3000ms最终这个请求的耗时冲到6200ms。这种请求占总流量可能只有0.2%但TP999直接爆表。后来把超时改成更短的1000ms重试改为只对幂等接口生效TP999立刻降下来了。排查这类长尾问题常用手段是把超过阈值比如500ms的请求单独捞出来按调用链路上的各个Span耗时排序。不要看平均只看慢请求明细通常能很快发现是哪次外部调用把时间拖长的。5.3 压测结果显示TPS虚高问题出在哪这个话题最近在一些技术群里聊得挺多说某系统压测报告写着TPS到了2万结果上线后连5000都撑不住。所谓TPS虚高通常有几个来源。一个是统计口径问题。压测脚本在应用入口统计所有成功请求但这个请求可能只在本地内存里返回了数据根本没落库或者数据库批量提交10条INSERT合成一次事务提交TPS自然虚高。真正衡量一个写接口的事务能力应该统计到数据库层实际提交的事务数而不是应用层收到多少请求。另一个常见原因是压测数据太干净。全内存查询、缓存命中率100%、没有锁竞争、没有GC压力这种环境下测出的TPS没有任何参考价值。更离谱的是用多个压测机同时压不同接口最后把数值加在一起当系统容量汇报。这种情况不是纯技术问题更多是团队对性能模型的理解没对齐。5.4 排查问题时的指标组合速查表想快速定位问题我习惯先把几项指标摆在一起看判断方向再着手深入。下面这个组合是我常年用的现象最可能的原因优先验证手段QPS上升TP99同时上升容量接近上限或资源竞争加剧看CPU、内存、连接池使用率QPS不变TP99上升线程排队下游变慢或阻塞看线程池活跃线程数、GC日志QPS下降TP99上升慢请求占满线程新请求无法处理看队列长度和拒绝次数TP99正常TP999飙升重试、超时、偶发GC捞全链路慢Trace按Span拆分耗时TPS比QPS高出很多单个请求包含多个内部事务核对事务定义口径QPS高但数据库TPS很低大量读请求或缓存拦截了写逻辑按接口拆分请求类型这张表不能覆盖所有情况但能帮你在一开始选对排查路径避免在错误的方向上消耗大量时间。6. 说点题外话指标治理比监控本身更花功夫到现在为止讲的都是“怎么看指标、怎么配告警”但真正要把这套体系用起来还差一步指标治理。我见过不少团队监控大盘做得非常漂亮CPU、内存、QPS、TP99一应俱全但一次线上故障复盘时发现核心接口没有按上游调用方拆分导致某一个广告劫持流量把所有调用方的TP99都拖高了却查不出是谁拖的后腿。这就是指标粒度不够细导致的盲区。实操里我建议每新增一个核心接口都同步定义清楚接口的黄金指标QPS、TP50、TP99、TP999、错误率再配合线程池水位、数据库耗时、下游RPC耗时这几个维度。接口上线之前先接好监控压测时顺便把基线数据跑出来。别等项目出问题了再补监控到那时候连对比的基线都没有。再分享一个小技巧排查TP类问题时把慢请求的条件阈值定在TP99和TP100之间的档位比如取500ms或1秒单独捞明细。我在实际项目里经常通过这种方式发现一些隐藏的坑比如某个接口在凌晨的定时任务清缓存瞬间耗时飙升比如某个网关偶尔会把连接置为半开状态导致重启握手超时。这些现象在平均指标和TP50里完全看不到只有在长尾维度上才会暴露。说白了这套指标体系的终极意义不是用来展示而是用来发现问题。TP50和TP999之间如果长期存在巨大差距系统的稳定性一定还有提升空间。学到的最好状态就是形成条件反射看到某个指标异常脑子里自动浮现出对应的排查方向。
返回列表