最初 Redis 用于同步的命令是SYNC,每次重连执行该命令时都会生成、传输、加载整个完整的 RDB 快照,严重占用机器资源与网络带宽。为了解决这一问题,后续版本的 Redis 追加了PSYNC命令,该命令支持以下两种同步模式:
全量重新同步Full-ReSync
slave 首次连接 master
master 与 slave 之间的状态差异过大
部分重新同步Partical-ReSync
网络抖动导致同步连接断开重连
sentinel 机制导致 master 节点发生变更
数据结构下面看看 redisServer 中与PSYNC相关的数据结构:
struct redisServer { /* * 节点ID 与 复制偏移量 * * 若当前节点是 master * server.replid 就是 server.runid * * 若当前节点原本是 master,转化为 slave 节点后 * server.replid 与 server.master_repl_offset 会被新 master 的同步信息覆盖 * * 若当前节点原本是 slave,被提升为 master 节点后 * rserver.eplid2 与 server.second_replid_offset 会记录当前节点作为 slave 时的同步信息 */ char runid[CONFIG_RUN_ID_SIZE+1]; /* 当前节点的运行时ID(每次重启都会发生变化) */ char replid[CONFIG_RUN_ID_SIZE+1]; /* 当前 master 节点的 runid */ char replid2[CONFIG_RUN_ID_SIZE+1]; /* 当前 master 节点作为 slave 节点时连接的 master 的 runid */ long long master_repl_offset; /* 当前 master 节点的复制偏移量 */ long long second_replid_offset; /* 当前 master 节点作为 slave 节点时的同步偏移量 */ /* * 复制积压缓冲 * * master 只维护一个全局的 server.repl_backlog,由所有 slave 节点共享 * 为了减少内存占用,server.repl_backlog 仅在 slave 节点存在时按需创建 */ char *repl_backlog; /* 复制积压缓冲(环形缓冲)*/ long long repl_backlog_size; /* 积压缓冲大小 */ long long repl_backlog_histlen; /* 积压数据长度 */ long long repl_backlog_idx; /* 积压缓冲尾部(可写位置)*/ long long repl_backlog_off; /* 积压缓冲首字节对应的同步偏移量(master offset)*/ } 运行ID无论主从,每个 Redis 服务器会在启动时生成一个长度为 40 的十六进制字符串作为运行IDrunid:
当 slave 首次请求同步时,会将 master 返回的server.runid保存至server.replid
当 slave 重新请求同步时,会将之前保存的server.replid发送给 master:
如果该 ID 与当前 master 的server.runid不一致,则必须执行一次全量重新同步
如果该 ID 与当前 master 的server.runid一致,则可以尝试执行部分同步操作
复制偏移量主从双方都会维护一个单位为字节的复制偏移量offset,通过该偏移量可以判断主从间的状态是否一致:
master 向 slave 传播 N 字节数据后,会将自己的复制偏移量增加 N
slave 接收到 master 传来的 N 个字节数据时,会将自己的复制偏移量增加 N
当 master 接收到 REPLCONF ACK 中的偏移量时,可以据此判断发送给 slave 的数据是否发生了丢失,并重发丢失的数据。
积压缓冲