小包延迟从哪来
客户端连续写小包时可能触发 Nagle 算法与延迟确认的交互:发送方等待前一个小包被确认才发下一个,接收方又延迟确认等待能否凑批,两者叠加会给交互加上数十毫秒量级的等待,表现出来像随机出现的固定延迟。多数成熟客户端库默认开启 TCP_NODELAY 规避这一点,自研客户端需要显式设置该 socket 选项。
基本信息
| 触发场景 | 小包连续写叠加对端延迟确认 |
|---|---|
| 典型表现 | 数十毫秒量级的附加延迟,看似随机 |
| 解决方式 | 客户端 socket 设置 TCP_NODELAY |
| 服务端侧 | 服务端连接通常已按优化方式配置,问题多出在客户端 |
| 验证方式 | 对比开关该选项前后的延迟分位 |
典型使用场景
- 自研客户端:直接操作 socket 时容易漏掉这个选项
- 延迟毛刺排查:出现固定量级毛刺时先排除这一项
- 小命令高频调用:小包占比高时更明显
- 客户端库升级:确认默认选项在新版本中仍按预期生效
- 多语言栈混用:不同语言库的默认行为不一致,需要逐一确认
验证与排查
验证与排查
# 客户端 socket 选项示意(以所用语言与库为准)
# setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, 1)
redis-cli --latency-history -i 5 # 看延迟时间序列,而不是单次结果
redis-cli INFO latencystats # 服务端侧分命令延迟分位
redis-cli INFO commandstats | grep usec_per_call # 单命令平均耗时
redis-cli SLOWLOG GET 10 # 排除服务端慢命令造成的排队判断线索
| 现象 | 可能原因 | 下一步 |
|---|---|---|
| 固定量级毛刺 | 小包与延迟确认交互 | 确认客户端选项是否设置生效 |
| 延迟随时段波动 | 对端排队或负载升高 | 查慢日志与服务端命令耗时 |
| 只有部分客户端慢 | 客户端实现差异 | 对比不同语言库的默认设置 |
| 只有批量调用慢 | 单次命令过大 | 拆分批次,而不是继续调 socket 选项 |
服务端侧连接一般已做过这层优化,看到延迟毛刺先确认客户端类型与库版本,问题多出在自研实现上。
把所有小包延迟都归因于 Nagle 会漏掉真正的排队:先看命令耗时分布与慢日志,再判断是否属于客户端实现问题。