你点了“发送”,消息 200ms 就到,对方马上看到“正在输入”;你等了 2 秒才看到,体验立刻像穿越回拨号上网时代。
这就是很多系统要做的取舍:少追一点总吞吐,换更短的单次等待时间,也就是“吞吐换延迟”。
先把概念说人话
延迟(Latency):一条请求从发出到收到结果的时间。
生活类比:你在咖啡店排队,自己这一杯多久拿到手。
小案例:客服系统里,用户点击“转人工”后 300ms 出现坐席状态,比 1.5s 更容易留住用户。
吞吐(Throughput):单位时间内系统能处理多少请求。
生活类比:食堂一小时能出多少份餐。
小案例:日志平台每秒处理 5 万条写入,吞吐高,但并不代表每条都回得快。
吞吐换延迟:主动牺牲一部分“每秒处理量”,换取更短的“单次响应时间”。
生活类比:公交车不等坐满就发车,整体运客量可能下降,但你更快到站。
小案例:在线协作编辑把批量刷盘改成小批次+短窗口后,光标同步明显更跟手。
为什么这招有效:把“等”拆出来看
很多链路慢,不是算得慢,而是“等得久”:等凑批、等队列、等聚合窗口结束。
吞吐优先链路(常见)
请求到达 -> 进入队列 -> 等批次凑满 -> 等聚合窗口结束 -> 写入/处理 -> 返回
延迟优先链路(调优后)
请求到达 -> 短队列 -> 小批次或直写 -> 短聚合窗口 -> 返回
先做这一步动作:把你的链路画成“等待点清单”,优先砍掉等待时间最长的一段。
四个常用手段,怎么用才不翻车
1) 小批次:少等人齐,先发车
是什么:降低 batch size 或缩短 flush 间隔,减少“凑满再处理”的等待。
像什么:电梯不再坚持“满员才走”,8 个人也先下楼。
小案例:消息回执服务把 batch_size 从 200 降到 20,P95 延迟明显下降,但每秒处理量跟着回落。
2) 实时直写:到站即办,不攒单
是什么:请求到达后直接写入目标存储或核心链路,不经过长时间中间缓冲。
像什么:超市开“快速结账通道”,你买两样东西不用排大队。
小案例:交易状态变更采用直写后,前端“支付成功”状态更快可见,但磁盘 I/O 次数增加。
3) 减少队列深度:别把队伍拉得太长
队列深度(Queue Depth)是什么:系统允许排队的请求数量上限。
像什么:餐厅门口只发 50 个号,超过就提示稍后再试,避免店里挤爆。
小案例:网关把队列深度从 1000 调到 100,排队等待时间下降,超载时会更早触发限流。
4) 减少聚合等待:窗口别开太久
聚合等待是什么:为了合并统计或批处理,故意等一个时间窗再统一处理。
像什么:报表不必“整点才出一次”,改成更短周期滚动刷新。
小案例:实时看板把聚合窗口从 100ms 缩到 10ms,图表更实时,但后台计算频次上升。
什么时候该果断用,什么时候先别动
| 场景 | 主要目标 | 建议策略 | 备注 |
|---|---|---|---|
| 聊天、协作、输入联想 | 交互跟手感 | 小批次 + 短窗口 + 必要时直写 | 用户对“卡一下”极敏感 |
| 支付状态、下单结果回显 | 结果尽快可见 | 关键链路直写 + 浅队列 | 先保状态一致再提速 |
| IoT 告警、风控提示 | 早发现早处理 | 降队列深度 + 缩聚合等待 | 宁可多算,别晚报 |
| 离线报表、历史归档 | 总量处理能力 | 大批次 + 深队列 | 这类通常吞吐优先 |
按这个表先判断业务类型,再决定是否上“吞吐换延迟”,不要一把梭全链路切换。
一套可复现的调优走法(30 分钟版)
别一上来就“全改”。先做可验证的小步试验:
- 记录基线:
P50/P95/P99 延迟、QPS、CPU、I/O。 - 每轮只改一个旋钮,压测 5~10 分钟:
batch_size: 200 -> 50 -> 20flush_interval: 50ms -> 10ms -> 5msqueue_depth: 1000 -> 300 -> 100aggregation_window: 100ms -> 20ms -> 10ms
- 给关键请求开直写旁路,只覆盖“实时性优先”流量。
- 设回滚阈值:如果吞吐跌幅超过可接受值(比如 20%),回退上一步。
- 固化两套策略:
- 交互链路:延迟优先
- 后台批处理:吞吐优先
这套动作的重点是:每次只动一颗螺丝,避免“变快了,但不知道为什么快”。
代价别回避:你是在用资源换体验
这类优化的账很清楚:
- 总吞吐通常会下降(单位时间处理量变少)。
- 资源利用率会降低(CPU、I/O 更碎片化,批处理效率下降)。
- 系统可能更早触发限流或背压(因为队列更浅、缓冲更少)。
但对实时交互链路来说,这笔账常常值得:用户感知的是“有没有立即响应”,不是你后端是不是跑在理论峰值。
最后带走 5 个可执行动作
- 先
画出等待点,定位最耗时的“等”。 - 用
小批次先试,不要全链路一起改。 - 给实时交互请求
开直写通道,和离线链路分开。 - 对队列和窗口设置
明确上限,并配套限流策略。 - 每轮都
测量并验证:延迟降了多少、吞吐掉了多少、是否在可接受范围内。
如果你只记一句话:实时性优先的短交互链路里,吞吐不是王,稳定且可感知的低延迟才是王。