Python、MongoDB、Redis集成测试与TCP乒乓阈值相关的Linux内核性能问题问询
大家好,最近我碰到了个挺折腾的性能问题,排查了好一阵还是有点困惑,想在这里问问有没有朋友遇到过类似情况,或者能给我点思路。
先说说我的场景:我维护着一个Python 3.12的应用,用PyTest写的集成测试链,涉及MongoDB(依赖PyMongo和Motor)和Redis。所有组件——测试代码、应用本身、Mongo、Redis——都跑在本地Docker Compose环境里,完全没有跨物理主机的网络通信。而且我确认过,PyMongo客户端默认就开启了TCP_NODELAY和SO_KEEPALIVE:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
奇怪的性能差异
这套测试在Ubuntu 20.04(内核5.4)上能跑出3倍的性能提升,但换到Ubuntu 22.04+,或者内核版本在5.10.135及之后的Linux系统上,这个性能优势就彻底消失了。
内核提交的排查过程
我对Linux内核做了二分查找,终于定位到了关键的内核提交:
- 最初是5.1版本里的
tcp: change pingpong threshold to 3这个提交带来了性能提升 - 但在5.10.135版本,这个提交被官方回滚了
- 到了2023年的6.7内核,官方又引入了
net.ipv4.tcp_pingpong_thresh这个sysctl参数,默认值设为1,允许用户手动配置这个阈值
尝试解决但遇到的问题
我本来以为把net.ipv4.tcp_pingpong_thresh改成3就能恢复之前的性能,但试了之后发现完全没用。反复测试后才发现,问题出在引入这个sysctl参数的提交上:如果我先回滚5.10.135的那个回滚提交,再应用这个sysctl参数的提交,就能重新获得预期的3倍性能提升。
于是我自己写了个小补丁,确实把性能拉回来了,补丁内容如下:
From: Oleg Neumyvakin <oneumyvakin@gmail.com> Date: Sun, 27 Jul 2025 20:40:47 +0700 Subject: [PATCH] Fix tcp pingpong threshold --- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_output.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f603ad9307af..fe5b1545c499 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3288,7 +3288,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_syn_linear_timeouts = 4; net->ipv4.sysctl_tcp_shrink_window = 0; - net->ipv4.sysctl_tcp_pingpong_thresh = 1; + net->ipv4.sysctl_tcp_pingpong_thresh = 3; return 0; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8e6ebf35ed58..912080d8d80a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -167,13 +167,16 @@ static void tcp_event_data_sent(struct tcp_sock *tp, if (tcp_packets_in_flight(tp) == 0) tcp_ca_event(sk, CA_EVENT_TX_START); - tp->lsndtime = now; - /* If it is a reply for ato after last received - * packet, increase pingpong count. + * packet, increase pingpong count. + * If this is the first data packet sent in response to the + * previous received data, */ - if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato) + if (before(tp->lsndtime, icsk->icsk_ack.lrcvtime) && + (u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato) inet_csk_inc_pingpong_cnt(sk); + + tp->lsndtime = now; } /* Account for an ACK we sent. */ -- 2.43.0
困惑的点
不过有个奇怪的地方:我用Python或者YCSB做了Mongo和Redis的合成测试,却完全测不出性能差异,只有在实际的集成测试场景下才会出现这个问题。
所以最后想问问大家:有没有人在使用Python、MongoDB、Redis的组合时,碰到过和TCP乒乓阈值相关的性能问题?或者对我这个补丁的思路有什么看法?
内容来源于stack exchange

