先确认是否真为网络延迟:若read_master_log_pos与exec_master_log_pos差值持续扩大、slave_io_state卡在等待事件、slave_io_running为yes但无推进,则基本锁定网络层问题;再通过iperf测吞吐、mtr看rtt抖动验证,启用slave_compressed_protocol=1并调优tcp内核参数。

确认延迟是否真由网络引起
别一看到 Seconds_Behind_Master 持续上涨就改 MySQL 参数。先用 SHOW SLAVE STATUS\G 看三个关键字段:Read_Master_Log_Pos 和 Exec_Master_Log_Pos 差值是否越拉越大;Slave_IO_State 是否卡在 Waiting for master to send event 或 Queueing master event to the relay log;Slave_IO_Running 是不是 Yes 但实际没推进。这三个现象同时出现,基本能锁定是网络层拖住了 IO 线程。
再补两步验证:
- 用
iperf -c 主库IP -p 5201 -t 60测从库到主库的实际吞吐,结果若明显低于主库 binlog 写入速率(比如主库每秒写 6MB,iperf只跑出 2.3MB),就是带宽硬瓶颈 - 用
mtr --report 主库IP看 RTT 波动和丢包率,跨机房常见 20–80ms 且抖动 >15ms,这种链路不调内核参数,MySQL 配置再怎么调都白搭
启用 slave_compressed_protocol=1 降低传输负载
MySQL 默认不压缩复制流量,binlog 有多大,网络就传多大。跨机房时这非常吃亏——ROW 格式下一条 UPDATE 可能带几 KB 原始数据,全量发过去。开启压缩后,实测能压掉 30%–50% 的网络负载,而且零配置成本。
操作很简单:
- 在从库
my.cnf的[mysqld]段落加一行:slave_compressed_protocol = 1 - 重启 MySQL,或直接执行
SET GLOBAL slave_compressed_protocol = 1 - 注意:仅 MySQL 5.7.9+ 支持;旧版本必须升级,别试图打补丁
这个设置不需要主库配合,也不影响 binlog 格式、GTID 或复制一致性,压缩由 IO 线程自动协商启用。
调优 Linux TCP 内核参数提升高延迟链路效率
默认 TCP 行为对跨机房复制极不友好:Nagle 算法攒小包、CUBIC 拥塞算法在高 RTT 下收敛慢、空闲连接重进慢启动——这些都会让 dump 线程发得慢、IO 线程收得卡。必须在从库(推荐主库也配)上改内核参数:
-
net.ipv4.tcp_nodelay = 1:禁用 Nagle,让每个 binlog event 小包立即发出,不等凑满 MSS -
net.ipv4.tcp_congestion_control = bbr:Linux 4.9+ 才有,BBR 在 20ms+ RTT 下带宽利用率比 CUBIC 高 2–3 倍 -
net.ipv4.tcp_slow_start_after_idle = 0:避免长连接空闲后吞吐骤降(MySQL 连接池场景尤其关键) -
net.ipv4.tcp_rmem和net.ipv4.tcp_wmem调大,例如设为4096 262144 16777216,否则大 relay log 写入会被缓冲区卡住
所有修改用 sysctl -w 生效后,记得写入 /etc/sysctl.conf 持久化。注意:BBR 不兼容某些老旧交换机的 ECN,若观察到大量 TCP retransmission,需切回 cubic。
避开 binlog_format = ROW 加剧网络压力的陷阱
很多人以为 ROW 格式更安全就无脑启用,但在跨机房场景下,它会把整行变更(含未修改字段)全发过去,网络压力翻倍。尤其当表宽、字段多、更新频繁时,Read_Master_Log_Pos 推进速度肉眼可见变慢。
更务实的做法是:
- 主库设
binlog_row_image = MINIMAL:只记录真正变化的列,体积直降 40%+ - 避免在主库执行
UPDATE t SET a=a, b=b WHERE id=1这类“伪更新”,它照样生成完整 ROW event - 如果业务允许,评估是否可阶段性切回
MIXED:对确定安全的语句走 STATEMENT,其余走 ROW
真正的瓶颈从来不在 SQL 线程并发数,而在于 relay log 能不能及时从主库“拿”过来——网络参数没调对,slave_parallel_workers 设成 64 也没用。











