第5篇容量场景实战——混合业务模型与 40000 TPS 系统容量系列《RESAR 性能工程实战——从 0 到 40000 TPS 的调优全记录》环境4 台华为云 FlexusX8C16GUbuntu 24.04内网千兆JMeter 5.6.3 Spring Boot 3.2.5 mall-demo MySQL 8 Redis 7 Prometheus/Grafana数据来源results/test_data_summary.md本篇所有性能数字均为真实压测结果禁止编造引言前四篇我们做完了两件事第 2 篇把 4 台机的全链路性能工程环境搭好第 3、4 篇用基准场景把单个接口的最大 TPS 天花板和单点瓶颈一一摸清——首页/mall/index最大 51,000 TPS登录/mall/login调优后 26,197 TPS商品查询/mall/product加索引后从 180 飙到 10,660 TPS下单/mall/order/create调连接池后从 1,389 到 4,067 TPS。但基准场景有一个天生的局限它测的是没有别人跟你抢资源的理想态。生产环境里首页、商品、登录、加购、下单是同时发生的。当 7 个接口在同一台 8C16G 应用机上混合施压它们之间会抢 CPU、抢连接池、抢数据库——单接口的天花板之和绝不等于混合后的系统容量。这一篇我们正式进入 RESAR 方法论的容量场景Capacity Scenario用一套真实的生产化业务配比模型把 7 个接口按比例混合压测找到这台机器真实的系统容量水位并给出最佳工作点与最大容量点的判定方法论。读完本篇你能收获容量场景与基准场景的本质区别以及为什么单接口 TPS 不能简单相加业务配比模型怎么抽用生产流量比例 基准 TPS 天花板双输入推导而不是拍脑袋capacity_mixed.jmx的7 线程组真实结构以及 7 个线程参数t_index / t_login / t_product / t_cartadd / t_cartlist / t_ordercreate / t_orderlist怎么按比例分线程一组真实的梯度数据40 / 80 / 120 线程下的总 TPS、RT、P99、错误率以及80 线程 最佳工作点、120 线程 容量上限的判定全过程一个反直觉但非常重要的结论配置的权重 ≠ 实际 TPS 占比慢接口会掉队全局监控证据混合场景下app idle 25%~28%、db idle 41%~50%与单基准场景的资源画像差异最佳工作点 vs 最大容量点的量化判定法TPS 增幅 / RT 增幅比值法收口验证前面所有调优动作在容量场景下是否真的兑现成 40,000 TPS。一、容量场景 vs 基准场景到底差在哪1.1 定义对比在 RESAR 体系中两者是递进关系不是替代关系维度基准场景Benchmark容量场景Capacity目标单接口最大 TPS、最佳压力点混合后系统整体容量水位业务单一接口、隔离其他业务按生产配比混合多业务作用提供配比依据、定位单点瓶颈验证整体水位、找系统拐点干扰隔离几乎无资源竞争业务间相互竞争 CPU/连接/DB结论形态“这个接口能扛 X”“这套系统整体能扛 Y拐点在哪里”一句话基准场景回答单个零件多强容量场景回答整台机器能跑多快。1.2 为什么单接口 TPS 不能相加这是新手最容易踩的坑。假设基准测得首页 51,000 TPS几乎纯 Redis 轻 CPU 渲染商品 10,660 TPS索引后偏 DB 读登录 26,197 TPSDB 读 Redis 写 token下单 4,067 TPSDB 事务最重如果天真地把它们加起来你会得到一个理论总容量 ≈ 92,000 TPS。但这是错的而且错得很危险。原因有三个CPU 是共享的。首页和登录都吃应用机 CPU混在一起时它们的总 CPU 预算被摊薄不可能各自都跑到天花板。数据库连接池是共享的。所有写接口、大部分读接口都走同一个 HikariCP我们调到了 50下单 hungry 地占连接时登录就得等。数据库自身是共享瓶颈。商品查询和下单都压 MySQLDB 的 CPU/IO 预算被混合流量瓜分。所以容量必须真刀真枪地混合压出来不能靠算术。1.3 混合压测的第一个难题配比从哪来要混合先得回答每个接口给多少并发不能首页给 100 线程、商品也给 100 线程——它们的天花板差了 5 倍51,000 vs 10,660给同样并发只会让商品饿死、首页浪费。配比必须有一个可解释的来源。下一章我们讲怎么抽。二、业务模型抽取配比不是拍脑袋2.1 两个输入RESAR 的业务配比模型Business Mix Model由两个输入共同决定生产流量比例Production Traffic Ratio——线上真实的用户行为分布。电商天然读多写少大家疯狂逛首页、看商品但真正下单的少。这是我们配比的主驱动。基准 TPS 天花板Baseline Ceiling——第 3、4 篇测出的每个接口的最大 TPS。它用来做可行性校验如果生产比例要求某个慢接口占 50%而它的天花板只有 4,000 TPS那这个模型本身就不成立要么生产比例假设错了要么系统容量会被它锁死。两者结合才能得到一个既像生产、又跑得起来的配比。2.2 本项目的业务配比模型结合 mall-demo 的生产化假设一个典型电商大促前的流量画像我们确定如下 7 接口配比业务接口业务含义配比权重类型/mall/index首页37.5%读Redis 轻渲染/mall/product商品查询25.0%读DB 索引查询/mall/cart/list购物车查询12.5%读DB 查询/mall/login登录10.0%读写DB 查 Redis 写 token/mall/cart/add加购10.0%写DB 事务/mall/order/create下单10.0%写DB 事务最重/mall/order/list订单列表5.0%读DB 查询读 : 写 ≈ 80% : 20%符合逛得多、买得少的真实电商特征。注意前三个读接口就占了 75%这正是首页/商品/购物车这类流量入口在生产中的绝对主导地位的体现。2.3 用基准天花板做可行性校验把配比和基准天花板摆在一起对照接口配比权重基准最大 TPS校验结论index37.5%~51,000天花板极高37.5% 权重完全吃得下product25.0%~10,660天花板中等25% 权重偏高但可接受靠 DB 索引优化已解瓶颈cart/list12.5%未单独基准同属 DB 读按生产比例给login10.0%~26,197天花板高无压力cart/add10.0%未单独基准写事务按生产比例给混合中看实际兑现order/create10.0%~4,067天花板最低DB 事务最重10% 权重是潜在短板混合中重点观察order/list5.0%未单独基准读按生产比例给校验要点没有哪个接口被配了超出其天花板一个量级的权重模型成立。下单/mall/order/create是天然的潜在短板——它基准只有 4,067 TPS却配了 10% 权重。如果生产真有 10% 的下单占比系统容量会被它死死锁住。这一点在第四章的权重 ≠ TPS现象里会被数据印证。2.4 一个关键认知权重是业务意图不是性能结果配比权重回答的是生产希望每个接口占多少而混合压测测出的是系统实际每个接口跑出多少 TPS。这两者天然不同——因为慢接口下单、加购即使用同样比例的线程也跑不出快接口的 TPS。这个落差恰恰是容量场景最核心的工程真相我们留到第四章用真实数据拆解。三、JMeter 实现7 个线程组按比例分线程配比模型确定后落地到 JMeter。我们生成了jmx/capacity_mixed.jmx由scripts/gen_jmx.py脚本产出。3.1 7 线程组的真实结构gen_jmx.py里容量场景的关键代码节选真实逻辑# ---- 容量场景混合业务模型 ----mixedHEADER.format(namecapacity-mixed)mixedthread_group(index-tg,t_index)http_sampler(GET /mall/index,GET,/mall/index)TG_END mixedthread_group(login-tg,t_login)csv_cfg(users.csv,username,password)\ http_sampler(POST /mall/login,POST,/mall/login,{username:${username},password:${password}})TG_END mixedthread_group(product-tg,t_product)csv_cfg(skus.csv,skuCode)\ http_sampler(GET /mall/product,GET,/mall/product,{skuCode:${skuCode}})TG_END mixedthread_group(cartadd-tg,t_cartadd)\ http_sampler(POST /mall/cart/add,POST,/mall/cart/add,{userId:${__Random(1,100000)},productId:${__Random(1,100000)},quantity:1})TG_END mixedthread_group(cartlist-tg,t_cartlist)\ http_sampler(GET /mall/cart/list,GET,/mall/cart/list,{userId:${__Random(1,100000)}})TG_END mixedthread_group(ordercreate-tg,t_ordercreate)\ http_sampler(POST /mall/order/create,POST,/mall/order/create,{userId:${__Random(1,100000)},productId:${__Random(1,100000)}})TG_END mixedthread_group(orderlist-tg,t_orderlist)\ http_sampler(GET /mall/order/list,GET,/mall/order/list,{userId:${__Random(1,100000)}})TG_END mixedFOOTER write(capacity_mixed.jmx,mixed)7 个ThreadGroup并发运行TestPlan.serialize_threadgroupsfalse每个用__P()读取对应的线程参数。7 个参数名与接口一一对应线程组线程参数取样器index-tgt_indexGET /mall/indexlogin-tgt_loginPOST /mall/loginCSV 参数化 users.csvproduct-tgt_productGET /mall/productCSV 参数化 skus.csvcartadd-tgt_cartaddPOST /mall/cart/addcartlist-tgt_cartlistGET /mall/cart/listordercreate-tgt_ordercreatePOST /mall/order/createorderlist-tgt_orderlistGET /mall/order/list3.2 为什么要 7 个独立线程组而不是一个线程组里放 7 个请求很多团队喜欢在一个线程组里用吞吐量控制器Throughput Controller去切比例。我们不这么做理由是独立线程组 独立并发数能精确兑现配比权重不被吞吐量控制器的概率采样引入随机误差每个接口能单独看 TPS / RT / 错误率排障时一眼定位是哪个接口拖后腿——这正是我们第四章要拆分权重 ≠ TPS的基础CSV 参数化按接口隔离登录用users.csv、商品用skus.csv互不串扰。3.3 梯度加压的真实运行命令容量场景我们压 120 秒/组梯度为 40 / 80 / 120 线程逐级加压ramp-up 20s避免瞬时冲击# 压力机 192.168.0.35 上执行jmeter-n-tcapacity_mixed.jmx\-Jt_index28-Jt_product18-Jt_cartlist9-Jt_login7\-Jt_cartadd7-Jt_ordercreate7-Jt_orderlist4\-Jduration120-Jrampup20\-lresult_80.jtl-e-oreport_80t_index28对应首页 37.5% 权重t_product18对应商品 25%以此类推。3.4 三梯度的线程分配表线程数依比率取整分配整数线程无法严格等比取最接近的整数三梯度如下梯度总线程t_indext_productt_cartlistt_logint_cartaddt_ordercreatet_orderlist梯度一4012954442梯度二80281897774梯度三1204127141111115每个梯度的相对配比保持一致只是整体放大——这样才能干净地观察并发翻倍时系统容量怎么变。四、梯度结果分析真实数据4.1 总览表三梯度真实结果每组 120s全程0 错误总线程总TPSavg RTP99app idledb idle结论4033,0431.1ms8ms28%50%线性上升段8040,1401.8ms10ms25%44%最佳工作点12041,4432.7ms12ms28%41%TPS 仅 3%RT 50% →容量上限关键结论先摆出来最大稳定容量 ≈ 40,000 TPS出现在 80 线程120 线程时 TPS 几乎不再增长RT 却明显恶化系统已达容量上限。4.2 JMeter 真实输出片段80 线程那组的 JMeter 聚合摘要吞吐字段即总 TPS错误率 0%summary 4816800 in 00:02:00 40140/s Avg: 1 Min: 0 Max: 98 Err: 0 (0.00%)40 线程组summary 3965160 in 00:02:00 33043/s Avg: 1 Min: 0 Max: 71 Err: 0 (0.00%)120 线程组summary 4973160 in 00:02:00 41443/s Avg: 2 Min: 0 Max: 121 Err: 0 (0.00%)三组的Err: 0 (0.00%)是容量场景最重要的健康基线——如果容量上去了但错误率飙升那根本不算容量叫雪崩。我们全程零错误说明40,000 TPS 是真实可用的系统容量不是以报错换来的虚高数字。4.3 80 线程分接口 TPS 拆解80 线程最佳工作点下7 个接口各自跑出的 TPS ——这是容量场景最有价值的明细数据接口配置权重实际 TPS实际占比类型/mall/index37.5%15,73739.2%读/mall/product25.0%9,88524.6%读/mall/cart/list12.5%5,93514.8%读/mall/login10.0%3,8879.7%读写/mall/order/list5.0%2,9987.5%读/mall/cart/add10.0%9512.4%写/mall/order/create10.0%7461.9%写最重合计100%40,139 ≈ 40,140100%—校验15,7379,8855,9353,8872,998951746 40,139与总 TPS 40,140 严丝合缝数据自洽。4.4 关键发现权重 ≠ TPS慢接口掉队现象把配置权重和实际 TPS 占比放一起看反差极其刺眼首页权重 37.5%实际 39.2% ≈ 完全兑现它太快了给多少线程基本都能跑满。商品权重 25%实际 24.6% ≈ 完全兑现索引优化后已无瓶颈。下单/mall/order/create权重10%实际只占1.9%——差了 5 倍多。加购/mall/cart/add权重10%实际只占2.4%——同样严重掉队。为什么因为下单是 DB 写事务扣库存 查价 插订单加购也是写事务它们的单请求成本最高即使用同样比例的线程数每秒也处理不了那么多请求。这就是第二章说的权重是业务意图不是性能结果。工程启示容量场景的真实 TPS 由最慢的那批接口决定下限。下单 加购两个写接口即便只配 20% 权重实际只贡献了 4.3% 的 TPS却占用了大量 DB 写能力和连接池。这正是基准天花板校验的价值——我们早在 2.3 节就预警下单是潜在短板数据印证了它。反过来想如果生产真的要求下单占 10%那么系统的真实可用容量会被下单锁在 ~700–1000 TPS 量级而不是 40,000。容量场景帮你提前看到这种结构性失衡。五、全局监控证据资源竞争长什么样容量场景最妙的地方是它能暴露混合施压和单基准施压在资源画像上的差异。我们全程用 Prometheus5s 抓取 Grafana mpstat看全局。5.1 资源 idle 三梯度一览梯度总线程app idledb idle说明4028%50%应用已较忙DB 还有余量8025%44%应用逼近瓶颈DB 余量收窄12028%41%应用 idle 没再降TPS 不涨了DB 仍在 41%监控查询PromQL取应用机192.168.0.153的 CPU idle100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle,instance192.168.0.153}[5s])) * 100)mpstat实时核级别确认应用机$ mpstat-PALL5# 80 线程时应用机整体 idle ≈ 25%ussy 占用明显无单核软中断集中# 对比第3篇单压首页 80 线程时 app idle 24.6% —— 混合场景资源画像与单基准量级相当5.2 与单基准场景的差异竞争来自内部把容量场景和单基准场景对照会发现一个反直觉的事实单压首页 80 线程app idle 24.6%TPS 50,958。混合 80 线程app idle 25%总 TPS 40,140。应用机 idle 几乎一样都在 25% 左右但总 TPS 却少了 1 万多。这 1 万多 TPS 去哪了答案是被混合流量内部的资源竞争吃掉了——首页本来能独占 51,000 TPS 的 CPU现在要和在场的商品、登录、下单共享那 75% 的忙碌 CPU首页自己的 TPS 被压到 15,737下单/加购这种重 DB 写的事务占着连接池和 DB 写能力连带拖慢了所有人的 RTDB idle 从单压首页时的较高水位降到混合时的 41%~50%——DB 也参与了竞争不再是某个单接口的专属后院。这正是单接口 TPS 不能相加的监控侧铁证资源是共享的竞争是内部的容量必须混合测。六、最佳工作点 vs 最大容量点判定方法论容量场景最容易被人误读的一句话是加压到 120 线程 TPS 还更高所以 120 更好。错。我们需要一套量化判定法而不是看 TPS 单调不减就觉得越大越好。6.1 TPS 增幅 / RT 增幅 比值法RESAR 用边际效率来判定拐点边际效率 ΔTPS% / ΔRT%当每多一档并发TPS 还在明显涨、RT 涨得温和→ 处于最佳工作区值得继续加压当TPS 几乎不涨、RT 却继续飙升→ 已过最佳工作点逼近容量上限再加压力纯属用延迟换没用的吞吐。注意分母用 RT 增幅而非绝对值——因为 RT 基数小毫秒级必须用相对增幅才有可比性。6.2 三梯度的边际效率计算区间ΔTPS%ΔRT%边际效率ΔTPS%/ΔRT%解读40 → 80 线程(40140-33043)/33043 21.5%(1.8-1.1)/1.1 63.6%0.34TPS 大涨RT 也涨但绝对值仍极低1.1→1.8ms性价比仍优80 → 120 线程(41443-40140)/40140 3.2%(2.7-1.8)/1.8 50.0%0.06TPS 几乎停滞3%RT 又涨 50%性价比崩塌把边际效率从 0.34 掉到 0.06降幅超 5 倍——这是教科书级的过了拐点信号。6.3 结论80 线程 最佳工作点120 线程 容量上限最佳工作点Recommended Operating Point80 线程 / 40,140 TPS。理由再往下加压TPS 增益3%完全配不上 RT 代价50%。工程上我们把 80 线程、≈40,000 TPS 定为推荐容量水位并据此为下一章的稳定性场景留出余量稳定性用 60% 容量 ≈ 24,000 TPS。最大容量点Max Capacity Point120 线程 / 41,443 TPS。这是使劲压能摸到的最高值但此时 RT 已 2.7ms比最佳点 50%、P99 12ms且 app/db idle 不再下降说明资源已无富余。这是上限不是日常该跑的水位。一句话方法论容量场景要找的从来不是最大能压到多少而是性价比拐点在哪里。最大值是天花板最佳点才是该住的地方。七、容量结论与运维建议7.1 系统容量结论经过基准调优索引 连接池 JVM后这台 8C16G 应用机的 mall-demo 系统最大稳定容量 ≈ 40,000 TPS80 线程0 错误P99 10ms容量上限 ≈ 41,400 TPS120 线程RT 恶化不推荐日常运行容量结构读占 ~86%写占 ~14%下单/加购两个重 DB 写事务是结构性短板推荐生产容量水位≤ 40,000 TPS并预留 20%~40% 余量给稳定性与突发。7.2 对前几篇调优动作的收口验证把前面所有调优在容量场景下做一次兑现度验收调优动作前几篇容量场景是否兑现证据商品查询加索引第 4 篇✅ 兑现容量中 product 跑出 9,885 TPS占 24.6%无全表扫描拖累连接池 10→50 堆 512m→4g Tomcat 200→400第 4 篇✅ 兑现40,000 TPS 下 0 错误、P99 仅 10ms连接池未成瓶颈首页软中断优化方向第 3 篇✅ 未复发混合 80 线程 app idle 25%无单核软中断集中导致的 RT 尖刺结论前几篇的每一个调优动作都在容量场景的 40,000 TPS 里得到了正向回报。性能工程不是调一个成一个的玄学而是每个改动都能在最终容量上被度量的闭环。7.3 给运维的可落地建议雏形容量水位红线生产网关/限流配置建议设在 40,000 TPS 以下如 32,000 TPS预留余量结构性短板治理下单/加购的 DB 写事务是容量天花板的根后续可走读写分离 / 异步化 / 分库分表进一步抬升索引上线审查沿用第 4 篇结论EXPLAINtypeALL禁止上线JVM / 连接池基线-Xms4g -Xmx4g -XX:UseG1GC、HikariCP 50、MySQLinnodb_buffer_pool_size4G、max_connections≥500。八、下一篇预告容量场景回答了系统能扛多少但它还留下两个没回答的问题稳定性40,000 TPS 冲 30 分钟会不会慢慢漏、慢慢错、内存慢慢涨我们要跑 30 分钟、60% 容量 ≈ 24,000 TPS 的稳定性场景看 t_order / t_cart 数据增长是否引发退化异常场景如果 DB 突然抖一下、Redis 挂一下系统是会雪崩还是能撑住第 6 篇《稳定性 异常场景实战与性能结论报告》我们会把 30 分钟稳定性压测、异常注入kill 掉 Redis / 制造 DB 慢查询、以及整套 RESAR 调优的最终性能结论报告一次性交给你。那是这个系列从能跑到敢上线的最后一跃。附录关键数字速查指标数值来源最佳工作点80 线程 / 40,140 TPS / avg RT 1.8ms / P99 10ms容量场景真实最大容量点120 线程 / 41,443 TPS / avg RT 2.7ms / P99 12ms容量场景真实全程错误率0.00%容量场景真实app idle容量25%~28%容量场景真实db idle容量41%~50%容量场景真实分接口 TPS 80Tindex 15737 / product 9885 / login 3887 / cart/list 5935 / cart/add 951 / order/create 746 / order/list 2998容量场景真实业务配比index 37.5% / product 25% / cart/list 12.5% / login 10% / cart/add 10% / order/create 10% / order/list 5%业务模型真实本篇所有性能数字均来自results/test_data_summary.md未做任何虚构。环境 IP 仅使用内网地址192.168.0.x不含任何公网 IP、密码或 token。