2. 延迟指标基础
在金融推理系统中,延迟不是一个单一数字,而是一组分布特征。很多团队在容量评估时只看平均延迟,结果上线后频繁出现“平均延迟达标、用户体验却很差”的割裂现象。要理解这一现象,必须先建立分位数思维:平均值描述整体趋势,而P95、P99、P999描述的是尾部风险。
一、核心原理:从平均值到分位数
假设一次批量推理请求共1000笔,每笔延迟按从小到大排列。平均值是所有延迟的算术平均;P50即中位数,表示50%的请求快于该值;P95表示95%的请求快于该值,只有5%的请求慢于它;P99表示仅1%的请求慢于它;P999则对应千分之一的最慢请求。分位数越高,越靠近长尾。
平均值对极端值不敏感,容易被大量快请求“拉低”,从而掩盖少数极慢请求。例如999笔请求耗时10ms,1笔耗时5000ms,平均延迟约为14.99ms,看起来非常健康;但P99约为10ms,P999却接近5000ms。如果这笔慢请求恰好是风控决策或交易下单,后果与平均值无关。
二、长尾延迟对金融业务的影响
金融业务对延迟的敏感度并非线性。交易、风控、投研三类场景对尾部的容忍度差异明显:
| 业务场景 | 关注指标 | 长尾延迟影响 | 典型容忍度 |
|---|---|---|---|
| 高频交易/下单 | P99、P999 | 错过报价窗口、滑点扩大、订单被拒 | P99 < 20ms,P999 < 50ms |
| 实时风控 | P95、P99 | 决策超时导致放行或拦截误判 | P95 < 50ms,P99 < 100ms |
| 投研因子计算 | P95、P99 | 批量任务超时、研究迭代变慢 | P95 < 2s,P99 < 5s |
| 行情推送 | P999 | 极端行情下推送积压、用户看到过期价格 | P999 < 100ms |
长尾延迟的危害在于“非对称放大”:一次P999抖动可能触发风控熔断、交易重试或用户投诉,进而引发连锁反应。尤其在开盘、收盘、财报发布等峰值时段,尾部延迟会从偶发变为频发。
三、SLA与SLO设计原则
SLA是服务等级协议,通常面向外部客户,带有法律或商业约束;SLO是服务等级目标,是内部可量化的目标值。设计时应遵循以下原则:
- 用分位数而非平均值定义目标。例如“P99延迟不超过80ms”,而不是“平均延迟不超过20ms”。
- 区分业务等级。交易链路、风控链路、投研链路应设置不同SLO,避免一刀切。
- 留出缓冲。SLO目标应略严于SLA承诺,为突发流量和故障恢复留出余量。
- 可观测、可告警、可复盘。每个SLO必须对应明确的监控指标、告警阈值和责任人。
- 定期校准。随着模型版本、硬件扩容和流量结构变化,SLO需要季度或月度回顾。
四、案例与误区复盘
某券商智能投研平台曾出现“平均延迟1.2秒,但用户投诉不断”的问题。排查发现,P99达到8秒,P999超过30秒。根因是因子计算任务在整点批量触发,与行情快照写入争抢I/O,导致少量请求排队。团队最初只看平均值,误判为“系统健康”。修正后,他们将SLO改为“P95 < 2s,P99 < 5s”,并引入队列隔离和限流,尾部延迟显著下降。
常见误区包括:
- 用平均值做容量规划,忽略尾部放大效应。
- 把P99当成“最差情况”,实际上P999和最大值可能更差。
- SLO设置过松,失去指导意义;设置过紧,导致频繁误告警。
- 只监控服务端延迟,忽略网络、排队和客户端重试带来的端到端延迟。
五、延迟指标计算示例
以下Python片段演示如何从延迟样本中计算常用分位数:
import numpy as np
latencies = np.array([12, 15, 18, 20, 22, 25, 30, 35, 40, 5000]) # 单位ms
print("平均值:", np.mean(latencies))
print("P50:", np.percentile(latencies, 50))
print("P95:", np.percentile(latencies, 95))
print("P99:", np.percentile(latencies, 99))
print("P999:", np.percentile(latencies, 99.9))
输出中平均值约521ms,P50为23.5ms,P99约4950ms。可见平均值被单个极端值严重拉高,而P50又完全掩盖了尾部问题。只有同时观察多个分位数,才能形成完整判断。
理解这张图的关键在于:曲线越往右,延迟越高,但样本越少。P50附近代表大多数请求的体验,P95/P99代表需要重点保障的尾部,P999则代表必须兜底的极端情况。金融推理系统的容量优化,本质上是在成本与尾部风险之间寻找平衡点。