Redis 6.0 的 io-threads 仅处理已建立连接的网络读写,不涉及持久化IO;RDB fork 或 AOF rewrite 引发的磁盘I/O竞争会拖慢主线程事件循环,导致 accept() 延迟、全连接队列溢出,表现为 Connection timed out。

Redis 6.0 的 io-threads 只管网络 I/O,不管持久化 IO;RDB fork 或 AOF rewrite 过程中产生的磁盘写压力,会直接拖慢主线程响应,导致 accept() 处理变慢,全连接队列(listen overflows)开始溢出——这不是网络丢包,是内核静默丢弃已完成三次握手的连接。
为什么持久化会卡住连接队列
主线程负责 accept()、命令执行、RDB fork/AOF rewrite 触发。当 RDB 生成或 AOF 重写启动时:
- fork() 系统调用本身不耗时,但后续子进程写磁盘会竞争同一块 I/O 资源(尤其是机械盘或高负载 SSD)
- 主线程在 fork 后仍需处理客户端请求,但若磁盘 IO 延迟高,会导致事件循环卡顿,
accept()调用频率下降 - 内核全连接队列积压,
netstat -s | grep "listen overflows"输出持续增长,客户端表现为Connection timed out,而非Connection refused - 此时
io-threads完全无效:IO 线程只加速已建立连接的数据读写,但新连接根本进不来
如何隔离持久化 IO 对连接处理的影响
核心思路是让持久化不干扰主线程的事件循环节奏。关键动作不是“关掉持久化”,而是错峰 + 降频 + 卸载:
- 禁用
save配置项,改用BGSAVE手动或定时触发(如 crontab 每 15 分钟一次),避开业务高峰 - 启用
aof-rewrite-incremental-fsync yes,让 AOF rewrite 子进程分批 fsync,避免单次大刷盘 - 将 RDB 文件路径挂载到独立 SSD 分区(非系统盘),并确保
vm.swappiness=1,减少 swap 干扰 - 对只读从节点,设置
no-appendfsync-on-rewrite yes,防止 AOF rewrite 期间阻塞 fsync - 若使用 Redis 7+,可开启
replica-serve-stale-data no配合replica-read-only yes,让从节点不承担写压力,专注同步和备份
验证是否真由持久化引发队列积压
不要只看 used_memory 或 connected_clients,重点盯三个指标:
-
INFO stats中的expired_keys和evicted_keys突增 → 表明内存压力触发频繁淘汰,间接加重主线程负担 -
INFO persistence中的rdb_bgsave_in_progress或aof_rewrite_in_progress为1且持续 >30 秒 → 持久化正在执行 -
INFO clients中的blocked_clients上升 +client_longest_output_list增大 → 主线程被阻塞,无法及时处理 client buffer - 配合系统命令:
pidstat -d -p $(pgrep redis-server) 1查看 Redis 进程的 IO wait%,若 >20% 且与listen overflows增长时间吻合,基本可锁定
连接队列积压后不能只调 somaxconn
增大 net.core.somaxconn 和 tcp-backlog 只是缓冲,治标不治本。如果主线程持续被持久化拖住,再大的队列也只是延缓崩溃时间:
- 临时缓解可用:
echo 65535 | sudo tee /proc/sys/net/core/somaxconn+redis.conf加tcp-backlog 65535,重启实例 - 但必须同步检查:
ss -lnt | grep :6379的 Send-Q 是否真变成 65535;否则说明配置未生效或 Redis 版本太老( - 更关键的是压测验证:用
redis-benchmark -t set -n 100000 -c 5000模拟并发建连,观察listen overflows是否停止增长 —— 如果仍涨,说明问题根因仍在持久化侧 - 线上环境慎用
CONFIG SET stop-writes-on-bgsave-error no:它会让 RDB 失败时不拒绝写入,但掩盖了磁盘 IO 已不可靠的事实
真正难的不是配参数,而是在业务写入高峰与持久化窗口之间找到那个微妙的平衡点——比如把 RDB 触发时机绑定到 slave_repl_offset 增速低于阈值的时段,或者用 redis-cli --rdb 把备份卸载到外部进程做。这些细节,往往比开几个 IO 线程更能决定 Redis 在高负载下的呼吸节奏。











