在排查网络问题时,不少工程师会在服务端用tcpdump抓取TCP交互报文,随后在输出中看到成片的SACK(Selective Acknowledgment)和DSACK(Duplicate SACK)标志。如果此时业务接口的响应时间平稳、错误率极低,单纯因为抓包里SACK多就认定网络故障,往往会误导后续运维动作。要理清这件事,需要先明白SACK和DSACK在协议层面代表什么,再结合Linux内核的重传与拥塞控制参数做针对性调整。

SACK与DSACK的协议原理及误读场景
TCP在基础确认机制里只能告知对端“到某个序列号之前的数据都已收到”,当中间报文乱序或重复到达时,接收方无法精确描述缺口。SACK选项允许接收方在ACK中附带已收到的非连续数据块范围,帮助发送方只重传真正丢失的段。DSACK是SACK的扩展,用来报告重复接收到的数据段,通常说明发送方发生了多余重传,或者链路出现了报文复制。
从协议设计看,SACK和DSACK的出现频率高,并不必然代表链路质量差。比如在多路径、无线切换、中间代理重组报文的网络里,轻微乱序就会触发SACK;而DSACK可能只是某次超时重传后原报文姗姗来迟。只要业务侧没有重传风暴和吞吐坍塌,内核其实已经用这些机制平稳消化了异常。理解这一点,才能避免“见SACK就慌”的误判。
我们可以通过简单命令观察本机SACK相关统计。以下脚本读取/proc/net/netstat中对应字段,帮助快速判断DSACK是否持续增长:
# 查看TCP扩展统计中的SACK与DSACK计数
grep 'TcpExt' /proc/net/netstat | awk '{
for (i=1; i<=NF; i++) {
if ($i ~ /SACK/) print $i
}
}'
Linux内核重传与SACK控制参数解析
Linux在/proc/sys/net/ipv4/下暴露了多个直接影响SACK行为的参数。tcp_sack控制是否启用SACK,默认值为1;设为0会关闭该能力,发送方在丢包时只能退回保守的退避重传,恢复效率明显下降。tcp_dsack控制接收方是否发送DSACK,默认也是1,关闭它能减少ACK报文长度,但会损失重复段检测能力。
tcp_reordering定义了TCP认为“正常乱序”的阈值,默认3。当接收方观测到的乱序程度超过该值,才判定为丢包并进入重传。如果网络天然存在较大乱序(如聚合链路、多队列网卡),适当调大该值,例如到10,可以显著减少因乱序导致的虚假快速重传,进而降低SACK/DSACK数量。
另一个关键参数是tcp_early_retrans与拥塞控制算法选择。以下示例展示如何临时修改重排阈值并查看当前拥塞算法:
# 临时将重排阈值调大到10 echo 10 > /proc/sys/net/ipv4/tcp_reordering # 查看当前拥塞控制算法 sysctl net.ipv4.tcp_congestion_control # 修改为bbr(需内核支持) sysctl -w net.ipv4.tcp_congestion_control=bbr
在长肥管道(高带宽高延迟)场景中,BBR算法基于带宽时延积建模,比传统Cubic更不容易因乱序误判丢包,从而压低SACK占比。但对于内网低延迟服务,Cubic配合合理reordering往往已足够。
面向业务正常的务实优化步骤
当tcpdump显示SACK/DSACK偏多但业务指标健康,第一步应是采集基线:记录当前tcp_retrans_segs、TCPSackRecovery等计数,并对照RTT和重传率。若重传率低于0.1%且延迟平稳,多数情况下无需关闭SACK,而是调优阈值与算法。
第二步可在测试节点上分步验证:先只调大tcp_reordering,观察DSACK是否回落;再评估切换拥塞算法后的CPU与延迟变化。切忌直接在生产全量关闭tcp_sack,这会让真实丢包恢复变慢。下面给出一段用于对比调优前后状态的Python采样代码:
import time
def read_counter():
# 伪代码:从/proc/net/netstat解析DSACK计数
val = 0
with open('/proc/net/netstat') as f:
for line in f:
if 'TcpExt' in line and 'DSACK' in line:
# 实际解析需按字段切分
val += 1
return val
base = read_counter()
time.sleep(60)
after = read_counter()
print('DSACK增量:', after - base)
最后,优化应以监控闭环为准。将SACK相关计数和业务黄金指标(错误率、P99延迟)同屏展示,只有当两者同向恶化时才升级为链路故障排查。通过这种分层思路,既能利用SACK提升丢包恢复,又能消除“大量SACK但业务正常”带来的不必要的告警噪音。