redis主线程在全量复制期间被阻塞,根本原因是fork子进程创建时需同步复制页表,内存越大、启用透明大页(thp)时阻塞越久,可达数百毫秒甚至秒级,导致客户端请求延迟激增或超时。

主从全量复制期间Redis主线程被阻塞
Redis 2.8 之后的全量复制(SYNC 或 PSYNC fallback 到 full resync)本质是 fork 子进程执行 bgsave,生成 RDB 文件。但关键点在于:fork 完成前,主线程需暂停所有写操作等待子进程创建完毕——这个“fork 阻塞”在内存大、页表多、启用了透明大页(THP)的机器上可能长达数百毫秒甚至秒级。
此时客户端发来的请求无法被处理,TCP 连接虽保持,但响应延迟激增,触发客户端超时(如 Jedis 默认 soTimeout=2000ms,Go 的 redis.Client 默认 ReadTimeout 常设为 3s)。
- 典型现象:
INFO replication显示master_last_io_seconds_ago突然变大,同时used_memory_rss在 fork 前后剧烈跳升 - 确认方法:用
strace -p $(pidof redis-server) -e trace=clone,wait4观察 fork 耗时 - 规避手段:关闭 THP(
echo never > /sys/kernel/mm/transparent_hugepage/enabled),或升级到 Redis 7.0+ 启用copy-on-write-friendly-fork(需内核支持)
RDB 文件传输占用大量带宽和CPU
从库发起全量同步后,主库将生成的 RDB 文件通过 socket 直接发送给从库。这个过程不经过 Redis 协议解析,是纯字节流传输,但会持续占用主线程的事件循环(Redis 6.0+ 引入 I/O 线程后仍由主线程负责发送)。
尤其当 RDB 文件超过 1GB、网络吞吐不足(如千兆网卡实际可用仅 80MB/s)、或从库磁盘写入慢(sync_file_range 或 fsync 延迟高)时,主库发送缓冲区堆积,client_longest_output_list 上升,进一步拖慢其他客户端响应。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 可观察指标:
net_input_bytes和net_output_bytes在INFO stats中突增;redis-cli --stat显示 output 峰值远高于平时 - 优化方向:限制 RDB 大小(拆分大实例)、使用 SSD 从库、主从部署在同一局域网(避免跨机房带宽瓶颈)
- 注意:
repl-backlog-size设得过大虽能减少全量复制概率,但本身也占内存且无法缓解当前传输压力
从库加载RDB时完全不可服务
从库收到完整 RDB 后,会清空当前数据集,然后由主线程单线程解析并重建内存数据结构。这个阶段从库 role 是 slave,但 connected_slaves 可能降为 0,且自身拒绝所有读请求(除非配置了 slave-read-only no,但极不推荐)。
如果你的应用做了主从读写分离,并把读流量打到了该从库,就会直接收到 LOADING Redis is loading the dataset in memory 错误——这不是超时,而是明确拒绝,但效果等同于超时失败。
- 检查方式:
redis-cli -h <slave_ip> INFO | grep loading</slave_ip>,返回loading:1即正在加载 - 不能靠
slave-serve-stale-data yes缓解此问题(它只控制主库宕机后的从库行为,不影响 RDB 加载期) - 真正可行的缓解:部署多个从库错峰同步(用
replica-priority控制选举顺序),或引入 Proxy(如 RedisShake、Twemproxy)自动摘除加载中的节点
为什么增量复制没生效?
全量复制本应是兜底行为,正常情况下主从靠 PSYNC 做增量同步。一旦出现全量,说明 repl-backlog 不够用或 runid 不匹配。此时客户端超时往往不是复制本身导致,而是背后暴露的架构隐患:
- 主库重启后 runid 变更,所有从库被迫全量 —— 应启用
replica-announce-ip/port并配合服务发现动态更新 - repl-backlog 过小(默认 1MB)或过短(
repl-backlog-ttl过早释放),导致网络抖动时从库 offset 落后太多而触发全量 —— 建议按主库写入 QPS × 平均命令大小 × 期望容忍中断时长预估,例如 10k QPS × 100B × 60s = 60MB - 从库长时间断连(如 GC STW、OOM Kill),backlog 已被覆盖,只能全量 —— 此时应监控
master_sync_in_progress和master_last_io_seconds_ago,设置告警而非等超时发生
真正棘手的是:这些条件常常叠加出现。比如一次 GC 导致从库断连 90 秒,backlog 又只有 1MB,主库恰好在期间写入了 50MB 数据,结果所有从库同时进入全量复制 —— 主库 fork + 发送 + 从库加载三重压力并发,整个集群雪崩式超时。










