MySQL网络性能需协同调优内核级(如net.core.wmem_max)和MySQL级(如max_allowed_packet)缓冲区;仅调大后者而忽略前者,会导致客户端send()时被内核静默截断,引发“Packet too large”错误。

MySQL网络套接字缓冲区不能靠“改一个参数就变快”,它实际由两层控制:Linux内核级的socket缓冲区(net.core.rmem_max等)和MySQL进程级的协议缓冲区(max_allowed_packet、net_buffer_length)。漏掉任意一层,都可能卡在半路。
为什么max_allowed_packet设大了,客户端还是报Packet too large
常见错误现象是mysqldump失败、LOAD DATA报错或ORM批量写入中断,日志里明确出现Packet for query is too large。这不是MySQL没读到包,而是客户端发不出去——因为Linux socket发送缓冲区(net.core.wmem_max)太小,内核在应用层调用send()时就截断了。
-
max_allowed_packet只控制MySQL服务端能接收/处理的最大单包尺寸,不控制OS底层能否把这么大一块数据顺利推到网卡 - 客户端工具(如
mysql命令行、mysqldump)也必须显式传参--max-allowed-packet=67108864,否则沿用默认值(通常4MB) - 若服务端设了64M但
net.core.wmem_max只有256KB,TCP栈会静默丢弃超出部分,连接可能直接断开,错误日志里反而看不到明显提示
Linux内核参数必须同步调高net.core.rmem_max和net.core.wmem_max
MySQL走TCP通信,其socket收发缓冲区上限受net.core.rmem_max和net.core.wmem_max硬性限制。即使MySQL内部允许64MB包,内核也不让分配超过wmem_max的发送缓冲区。
- 临时生效:
sudo sysctl -w net.core.rmem_max=33554432(32MB)、sudo sysctl -w net.core.wmem_max=33554432 - 永久生效:在
/etc/sysctl.d/99-mysql-network.conf里写两行,然后sudo sysctl -p - 注意
rmem_default/wmem_default也要相应调高(至少等于max的1/4),否则新建连接初始缓冲区太小,首包就容易触发重传 - 别盲目设1G——每个连接都会按需分配缓冲区内存,1000个并发连接 × 1GB = 直接OOM。建议按峰值流量×20%余量估算
net.ipv4.tcp_rmem和net.ipv4.tcp_wmem要不要动
这两个是TCP协议栈专用三元组(min default max),影响的是内核自动调优行为,不是MySQL直控项。多数场景下保持默认即可,除非你明确在跑跨地域长肥管道(BDP > 10MB)。
- 修改
tcp_rmem的max项不会突破net.core.rmem_max限制,只是告诉TCP栈“你可以往这个上限里动态扩” - 如果已调高
net.core.rmem_max,再把tcp_rmem第三项设为相同值,才能让高带宽链路真正利用大窗口(需同时确认net.ipv4.tcp_window_scaling=1) - 普通IDC或云VPC内网环境(RTT tcp_rmem = 4096 131072 6291456足够,改了反而干扰自适应逻辑
MySQL配置里net_buffer_length其实很少需要调
这个参数常被误认为“主缓冲区”,但它只是MySQL分配初始网络缓冲区的起点(默认16KB),后续会按需自动扩展到max_allowed_packet上限。真要调,也是为了减少小包分配次数,而非提升吞吐。
- 仅当观察到大量短查询(如单行INSERT)伴随频繁内存分配/释放时,才考虑把
net_buffer_length从16K提到64K - 调太高反而浪费:每个连接都预分配这块内存,哪怕只用几个字节
- 它和
max_allowed_packet无依赖关系,设成1M但max_allowed_packet是4M,大包照样被拒绝 - 验证是否生效:连接后执行
SHOW VARIABLES LIKE 'net_buffer_length';,注意这是会话级变量,全局设置需在[mysqld]段写
最容易被忽略的是客户端和服务端参数不一致,以及内核缓冲区与MySQL缓冲区之间存在隐性剪裁。调完记得用tcpdump抓包看实际TCP window size是否随负载增长——如果始终卡在64KB,大概率是net.core.rmem_max没生效或应用没正确调用setsockopt(SO_RCVBUF)。


















