Redis 的主从同步是构建读写分离、数据备份和高可用架构的基础能力。它的核心流程是主节点将写入命令传播给从节点,从节点执行相同命令来保持数据一致。但这一过程并非同步完成,默认情况下主节点不会等待从节点确认,只要命令写入本地内存就算成功,因此从节点数据天然滞后于主节点。业务上表现为主从读取不一致,比如刚写入的数据在从节点查不到,或者监控中的主从偏移量差值持续扩大。要分析延迟原因,需要先理解 Redis 主从复制的数据流经过哪些环节,以及每个环节可能引入等待和阻塞。

Redis 主从同步分为全量同步和增量同步两个阶段。全量同步通常在从节点首次连接或复制偏移量差距过大时触发,主节点生成 RDB 快照并发送给从节点,从节点清空旧数据后加载快照;增量同步则依赖主节点的复制积压缓冲区,将期间的写命令持续发送给从节点。无论哪个阶段,延迟本质上都来自三个方向:主节点生成和发送数据的耗时、网络传输耗时、从节点接收和处理数据的耗时。下面从这几个方向展开分析。
一、Redis主从同步的复制链路与延迟基线
Redis 的主从复制链路可以拆分为几个连续阶段。从节点首次建立连接时,会发送 PSYNC 命令携带自己的复制 ID 和偏移量。如果主节点判断无法进行增量续传,就会触发全量同步:主节点执行 BGSAVE 生成 RDB 快照,同时将新写入的命令记录到客户端输出缓冲区,RDB 生成完成后发送给从节点,从节点清空数据库并加载 RDB,随后主节点把缓冲区的增量命令继续发送给从节点。即使进入稳定增量同步阶段,命令也是先写入主节点的复制积压缓冲区,再通过网络异步推送给从节点。
这里有两个关键点决定了延迟无法避免。第一,Redis 默认的复制是异步的,主节点执行写命令后立即返回客户端,不会等待从节点确认。也就是说,从节点的数据状态永远滞后于主节点至少一个网络往返时间加上命令执行时间。第二,主从之间的命令传播并非逐条同步,而是批量发送,从节点收到后还要排队在单线程中执行,这又引入排队延迟。因此即使在局域网低延迟环境下,主从延迟通常也有亚毫秒到毫秒级;一旦出现大命令、慢查询或网络抖动,延迟会迅速上升到秒级甚至更高。
二、主节点侧导致延迟的关键因素
主节点侧的第一个高风险点是 RDB 快照生成。全量同步时,BGSAVE 会 fork 一个子进程来遍历内存数据并写入磁盘。fork 本身通常很快,但在内存占用较大或开启了 THP(透明大页)时,fork 耗时可能达到百毫秒甚至秒级,期间主进程会被短暂阻塞,所有写命令无法处理。即使子进程运行期间主进程不会被完全阻塞,但磁盘 IO 负载升高时,子进程写 RDB 的速度会直接决定全量同步耗时。如果 RDB 文件体积很大,网络传输时间也会显著增加。
第二个因素是复制积压缓冲区配置不足。主节点会维护一个固定大小的环形缓冲区 repl-backlog,记录最近的写命令。如果从节点断线时间较长,或者主节点写入流量远超缓冲区容量,从节点重连后会发现自己的偏移量已经不在缓冲区范围内,只能重新触发全量同步。全量同步本身成本很高,会进一步拉大延迟。可以通过 CONFIG GET repl-backlog-size 查看当前大小,结合 master_repl_offset 与 repl_backlog_first_byte_offset 判断是否覆盖了从节点的偏移量。
第三个容易被忽略的是客户端输出缓冲区。在主从复制中,主节点为每个从节点维护一个输出缓冲区,如果从节点处理速度跟不上,或者网络拥塞,主节点的输出缓冲区会不断积压。当积压超过 client-output-buffer-limit replica 的限制时,主节点会主动断开该从节点的连接,触发重新同步。这会让本已延迟的从节点雪上加霜。排查时可以在主节点执行 info clients 观察输出缓冲区占用。
# 查看复制积压缓冲区配置和当前状态 redis-cli -p 6379 CONFIG GET repl-backlog-size redis-cli -p 6379 CONFIG GET repl-backlog-ttl redis-cli -p 6379 INFO replication
三、从节点侧导致延迟的关键因素
从节点侧最常见的问题是单线程执行命令。Redis 从节点同样使用单线程处理来自主节点的命令流。如果从节点上还承担了读请求,某些慢查询会阻塞命令流的执行。例如在从节点上执行 KEYS *、SMEMBERS 遍历大集合,或者使用 SORT 对大数据量排序,都会让主节点推送的后续命令在队列中等待。尤其是当业务在从节点上执行统计类查询时,原本稳定的毫秒级延迟可能瞬间升到秒级。
第二个因素是 RDB 加载和持久化策略。在全量同步过程中,从节点需要把接收到的 RDB 文件写入磁盘,再加载到内存。如果开启了 RDB 落盘,但磁盘速度较慢,写入和读取都会成为瓶颈。Redis 提供了 repl-diskless-load 选项,可以设置为 on-empty-db 或 disabled。在磁盘性能较差的环境下,建议采用无盘加载,让从节点直接通过网络流解析 RDB,减少磁盘 IO。但无盘加载会增加网络带宽压力,需要根据实际场景权衡。
第三个因素是过期键处理和内存淘汰。从节点不会主动删除已过期的键,而是等待主节点发送 DEL 命令。如果主节点有大量键在短时间内过期,DEL 命令会集中传播到从节点,如果这些键本身很大或者数量很多,从节点执行删除时可能产生延迟。同时,如果从节点内存不足触发了 maxmemory 淘汰策略,虽然从节点一般禁止写入,但淘汰操作仍然可能占用 CPU,影响复制命令处理。
四、如何定位主从延迟并优化
定位延迟最直接的方式是监控主从偏移量差值。在从节点执行 info replication,观察 master_repl_offset 与 slave_repl_offset 的差值。如果差值稳定在很小范围,说明复制基本实时;如果差值持续扩大,说明从节点处理速度跟不上,或者主从网络出现问题。还可以结合 lag 字段查看从节点与主节点的网络延迟。一般建议在监控系统中对偏移量差值设置告警阈值,而不是只看连接状态。
优化方向上,首先要避免大 Key 和慢命令。大 Key 会导致全量同步耗时剧增,也会让增量同步过程中单条命令占用更多网络带宽和 CPU。可以通过 redis-cli --bigkeys 扫描大键,并改造业务数据结构。其次要根据写入流量合理调整 repl-backlog-size,一般建议设置为每秒写入字节数的 2 到 4 倍。例如写入流量峰值 10MB/s,backlog 至少设置为 20MB 到 40MB。再次,主从都尽量使用无盘复制参数:主节点设置 repl-diskless-sync yes,从节点设置 repl-diskless-load on-empty-db,在万兆网络或本地机房中可以显著降低全量同步时间。
对于读一致性要求较高的业务,可以引入 WAIT 命令。客户端在写操作后执行 WAIT 1 100,可以要求主节点等待至少 1 个从节点确认收到命令,最长等待 100 毫秒。这样虽然不能消除延迟,但可以保证读取从节点时数据已经同步。不过 WAIT 会增加写操作延迟,只能用于关键路径。更彻底的方案是在客户端路由层面做读写分离时,将写后立即读的请求发送到主节点,或者使用 Redis Cluster 的读写分离策略时保证槽位映射准确。
# 在从节点查看复制状态 redis-cli -p 6380 INFO replication # 关注以下字段 # master_repl_offset:18446744073709551615 # slave_repl_offset:18446744073709551400 # 差值 = master - slave = 215,说明有约215字节的延迟 # 客户端使用 WAIT 命令要求同步确认 redis-cli -p 6379 SET user:1001 'Alice' redis-cli -p 6379 WAIT 1 100
五、总结
Redis 主从同步延迟是异步复制架构下的必然现象,但通过合理配置和针对性优化,可以把延迟控制在业务可接受的范围。核心是要理解延迟来自主节点、网络、从节点三个环节,任何一个环节出现瓶颈都会反映为偏移量差值扩大。排查时先用 info replication 定位是主节点发送慢还是从节点处理慢,再结合 RDB 生成、backlog 大小、慢命令和网络指标逐层分析。对于强一致场景,单纯依赖主从复制不够,需要借助 WAIT 或客户端路由策略来弥补异步复制的不足。