Redis 是如何进行主从复制的?( 二 )


如果 Redis 设置了密码,slave 会发送 auth $masterauth 指令,进行鉴权 。当鉴权完毕,从库就通过 replconf 发送自己的端口及 IP 给 master 。
接下来,slave 继续通过 replconf 发送 capa eof capa psync2 进行复制版本校验 。如果 master 校验成功 。从库接下来就通过 psync 将自己的复制 id、复制偏移发送给 master,正式开始准备数据同步 。
主库接收到从库发来的 psync 指令后,则开始判断可以进行数据同步的方式 。
前面讲到,Redis 当前保存了复制 id,replid 和 replid2 。如果从库发来的复制 id,与 master 的复制 id(即 replid 和 replid2)相同,并且复制偏移在复制缓冲积压中,则可以进行增量同步 。master 发送 continue 响应,并返回 master 的 replid 。slave 将 master 的 replid 替换为自己的 replid,并将之前的复制 id 设置为 replid2 。之后,master 则可继续发送,复制偏移位置 之后的指令,给 slave,完成数据同步 。
如果主库发现从库传来的复制 id 和自己的 replid、replid2 都不同,或者复制偏移不在复制积压缓冲中,则判定需要进行全量复制 。master 发送 fullresync 响应,附带 replid 及复制偏移 。然后, master 根据需要构建 rdb,并将 rdb 及复制缓冲发送给 slave 。
对于增量复制,slave 接下来就等待接受 master 传来的复制缓冲及新增的写指令,进行数据同步 。
而对于全量同步,slave 会首先进行,嵌套复制的清理工作,比如 slave 当前还有嵌套的 子slave,则该 slave 会关闭嵌套 子slave 的所有连接,并清理自己的复制积压缓冲 。
然后,slave 会构建临时 rdb 文件,并从 master 连接中读取 rdb 的实际数据,写入 rdb 中 。在写 rdb 文件时,每写 8M,就会做一个 fsync操作, 刷新文件缓冲 。当接受 rdb 完毕则将 rdb 临时文件改名为 rdb 的真正名字 。
接下来,slave 会首先清空老数据,即删除本地所有 DB 中的数据,并暂时停止从 master 继续接受数据 。然后,slave 就开始全力加载 rdb 恢复数据,将数据从 rdb 加载到内存 。
在 rdb 加载完毕后,slave 重新利用与 master 的连接 socket,创建与 master 连接的 client,并在此注册读事件,可以开始接受 master 的写指令了 。
此时,slave 还会将 master 的 replid 和复制偏移设为自己的复制 id 和复制偏移 offset,并将自己的 replid2 清空,因为,slave 的所有嵌套 子slave 接下来也需要进行全量复制 。
最后,slave 就会打开 aof 文件,在接受 master 的写指令后,执行完毕并写入到自己的 aof 中 。
相比之前的 sync,psync2 优化很明显 。在短时间断开连接、slave 重启、切主等多种场景,只要延迟不太久,复制偏移仍然在复制积压缓冲,均可进行增量同步 。
master 不用构建并发送巨大的 rdb,可以大大减轻 master 的负荷和网络带宽的开销 。同时,slave 可以通过轻量的增量复制,实现数据同步,快速恢复服务,减少系统抖动 。
但是,psync 依然严重依赖于复制缓冲积压,太大会占用过多内存,太小会导致频繁的全量复制 。而且,由于内存限制,即便设置相对较大的复制缓冲区,在 slave 断开连接较久时,仍然很容易被复制缓冲积压冲刷,从而导致全量复制 。

【Redis 是如何进行主从复制的?】


推荐阅读