资源画像与成本:CPU 不满,接口为什么仍然慢
CPU 只用 22.5%,接口却在变慢——瓶颈在网络带宽、连接池或锁上。本页建立单位请求的资源画像,把"优化是否值得"换算成同窗口、同成功定义下的单位成本。队列与瓶颈迁移的分析见队列网络、扇出与瓶颈迁移;质量目标与 SLO 的口径见SLI、SLO 与预算。
瓶颈资源决定优化方向
性能优化先判定瓶颈资源:CPU、内存、磁盘 IOPS、网络带宽、锁争用、连接数或依赖容量。更换算法只在瓶颈受该算法控制时有效。单位请求成本可以分摊计算、存储、网络、许可证和运维值班,但成本需绑定相同服务目标和可靠性水平;降低成本若增加事故率或人工操作,不一定降低总成本。
完整案例:CPU 不满,接口为什么仍然慢
Java 图片元信息处理服务每秒 600 次任务,8 个有效 CPU 核心,单任务 CPU 3ms、读取对象存储 200KiB、写数据库 4KiB、连接持有 20ms。按需求模型,CPU 为 600×0.003=1.8 核秒/秒,即 8 核的 22.5%;对象读取约 600×200KiB=117.1875MiB/s,已接近假设的 1Gbit/s 链路理论约 119.2MiB/s,协议、加密、竞争尚未扣除。CPU 空闲不代表网络还有余量。
数据库平均连接占用 600×0.02=12,若连接池只有 10,可能出现获取连接等待;提高池到 20 可能有效,也可能把瓶颈搬到数据库 I/O。一个请求同时读取十个对象会增加字节量与扇出,不能因为逻辑请求数没变就沿用成本画像。上述需求均为教学假设,应由资源计数与请求样本替换。
资源画像不是一张 CPU 截图。CPU 需要区分进程使用、容器配额和节流;内存需要区分存活对象、分配速率、队列保留与直接内存;磁盘需要关注读写大小、随机性、吞吐、IOPS 和 fsync 延迟;网络需要关注有效负载、重传、连接建立与跨区流量;锁需要持有时长、竞争对象和排队。均值可做初算,尾部和突发需要单独检查。
从可观察证据建立因果假设
假设发布压缩后网络降为 60MiB/s,但 CPU 上升到 7.5 核、P99 从 150ms 变为 600ms。不能只因流量费降低就宣布成功:压缩把瓶颈转向 CPU 或 GC。应比较相同请求大小与完成率,查看线程运行、分配和锁等待,再以小流量关闭压缩验证。发布与性能变化时间相邻只是相关,若同时缓存失效或对象变大,仍需排除混杂。
Java 可用 JFR 检查 CPU 样本、分配、监视器竞争和 socket 等事件,但事件是否启用、阈值和采样方式取决于配置。Java 17 的 jfr 命令支持汇总和过滤录制文件;命令工具不会自动生成所有缺失事件,也不能由采样频次直接精确算每个方法的绝对耗时。
可在隔离实验进程执行以下命令,先用 jcmd 确认 PID 属于实验,不把录制开销直接加到生产:
jcmd PID JFR.start name=capacity settings=profile duration=30s filename=/tmp/capacity.jfr
jfr summary /tmp/capacity.jfr
jfr print --events jdk.CPULoad,jdk.JavaMonitorEnter /tmp/capacity.jfrPID 是待替换占位符;是否有锁事件由实际竞争与配置决定,空结果不证明没有锁。本轮未对真实服务执行录制,以上为实验计划。将测得的请求区间与 JFR 区间对齐,结合吞吐、失败量和资源限额解释,避免跨进程、跨窗口相除。
两种优化方案及失败分支
方案 A 做压缩,将对象传输减少 50%,但每任务 CPU 增加 5ms,600 次/秒新增 3 核,总 CPU 需求从 1.8 变为 4.8 核。网络压力下降,仍要验证压缩格式、解压成本、客户端支持和尾部。若增加到 1,000 请求/秒,总 CPU 约 8 核,恢复余量为零,不能把 600 QPS 的收益线性外推。
方案 B 维持格式,减少重复读取并做受控缓存,若命中 60%,对象读取降至约 46.875MiB/s;新增内存成本、失效协议和陈旧风险。只缓存非裁决性元信息较安全,若把权限或删除状态也缓存,则必须约束授权有效期,删除不能被旧缓存"复活"。缓存冷启动时仍回到原始网络需求,应做冷缓存演练而非只测试热数据。
故障路径:一个热门对象过期,数百请求同时回源,网络瞬间满;应用积压增加存活对象,GC 抢 CPU,响应变慢触发重试,缓存虽最终填好仍有大队列。可按 key 合并回源、随机过期、限制并发并给旧值服务明确时限,但不能让合并者永久阻塞所有等待者。优化应同时给出异常恢复,而非只有正常收益百分比。
成本必须除以同窗口的成功业务量
假设当前每月应用 12,000 元、存储 6,000、流量 8,000、值班分摊 4,000,共 30,000 元,完成 90,000,000 个有效任务,单位百万成功任务成本为 30,000/90=333.33 元。优化后成本 27,000 元,但只成功 60,000,000,单位成本为 450 元,绝对账单下降而单位服务变贵。
不能用整月花费除以活动第一天完成量,也不能用尝试次数稀释失败率和单位成本。比较应固定 SLO、故障余量、保留期和成功定义;活动尖峰、待机容灾、日志保留、人力、许可证与退出费用按明确口径分摊。两种方案分母不同,应同时报告业务量变化与资源效率,不把需求下降误认为优化。
优化压缩节省每月 4,000 元流量、增加 2,000 元 CPU 和 1,000 元运维,净节省 1,000;开发投入假设 30,000 元,简单静态回本为 30 月,未计资金时间、风险和业务增长。若技术生命周期预计只有一年,可能优先选缓存或暂缓。成本数字是教学输入,不是云产品报价。
可运行模型与验证产物
运行 完整实验脚本:
python3 labs/capacity_models.py resource预期得到 1.8 核 CPU、117.1875MiB/s、12 个平均占用连接,以及 333.33 与 450 的单位成本,model=mean-demand-not-tail-profile。再将请求大小翻倍,网络需求翻倍但 CPU 是否翻倍未知,需测量;把峰值提高到 1,000 后,压缩方案 CPU 约 8 核,应触发重新规划。本轮该公开脚本及断言已运行;JFR 录制与真实压测未运行。
提交按请求类型的资源画像、瓶颈假设、相同窗口前后对照、冷缓存与故障结果、成本表和未验证项。可选专家深化为用实验设计分离压缩、缓存和数据大小的交互影响;架构师必须决定优化是否值得,以及谁承担新增复杂度和恢复义务。
输入替换与敏感性:画像成立的前提写在输入里
资源画像算例的结论(1.8 核、117.19MiB/s、12 个连接、单位成本 333.33 元)依赖一组具体输入。评审要问的不是"这个数对不对",而是"哪项输入变化会让结论翻转"。本页给出三个替换实验,把画像变成可复核的敏感性表。
实验一:对象读取大小从 200KiB 翻到 400KiB。CPU 不变(仍 1.8 核),网络从 117.19 升到 600×400/1024=234.375MiB/s,是原来的两倍,早已超过假设的 1Gbit/s 链路(约 119.2MiB/s)。这说明"CPU 只用 22.5%"完全不能推出"还有余量"——瓶颈在网络。若对象大小翻倍同时 CPU 不变,优化方向应从 CPU 转向带宽或缓存。
实验二:缓存命中率从 60% 降到 0%。对象读取从 117.19×0.4=46.875MiB/s 回到 117.19MiB/s,回到链路边缘。热缓存下的安全结论不能用于冷启动;冷缓存演练必须单独做,否则发布当天首次全量回源就是事故。
实验三:成功定义变化。原画像单位成本 30,000/90=333.33 元/百万成功任务;若成功定义改为"文件校验通过",成功量从 9,000 万降到 6,000 万,单位成本变 27,000/60=450 元。账单下降不等于经济性改善,分母的语义变化会直接改写结论。三项实验的共同点是:画像的每一项输出都必须绑定输入与成功定义,替换输入后重新计算,而不是沿用旧结论。
执行 python3 labs/capacity_models.py profile_inputs:输入原画像与三个替换。预期输出 network_200kib=117.1875、network_400kib=234.375、network_cache0=117.1875、network_cache60=46.875、unit_normal=333.33、unit_after=450。脚本只做算术,JFR 实测与压测未运行。
两种做法:A 用一组画像覆盖所有场景,快但把冷缓存、大对象与故障态排除在外;B 按"正常/冷启动/大对象/故障"四态各出一张画像,成本高但每项结论都有适用边界。本例选 B,并规定任何性能结论必须写明它属于哪一态;跨态引用即视为越界。
架构师变化条件
把网络从同区 1Gbit 换为跨区按流量计费:方案 A 的流量费收益放大,方案 B 的缓存失效协议成为合规与安全边界。若成功定义从"返回即成功"改为"文件校验通过才成功",压缩损坏会直接打击成功量——单位成本分母缩小,优化可能由赚变亏。峰值翻倍后重算两张画像,确认瓶颈迁移到 CPU 后的恢复余量;每次变化重报单位成本而非绝对账单。
闭卷验收
本页 8 道闭卷题收进了下方自测面板:先回忆,再展开参考答案与评分点,最后给三档自评。