
java后端领域
2023/03/26阅读:11主题:默认主题
redis 主从复制原理以及读写分离的坑
redis 主从复制原理以及注意事项
建立主从复制的方式
-
在配置文件中加入 slaveof [masterHost] [masterPort],随着 redis 启动生效 -
在redis 启动时加上参数:--slaveof [masterHost] [masterPort] -
直接使用命令:slaveof [masterHost] [masterPort]
断开复制
断开复制:slaveof no one,断开与 主节点复制,并且从节点晋升为主节点 切换主节点:slaveof [newMasterHost] [newMasterPort],断开与老主节点的复制,与新节点建立复制关系,删除从节点所有数据,对新节点进行复制。
主从复制的拓扑
-
1主1从 -
1主多从(从节点可以分担读压力,1个主节点需复制给多个从节点,压力大) -
树型结构的主从(可以减轻主从同步时对主节点压力,但是增加手动和自动故障转移的难度)
主从复制的原理
复制流程
执行slave of 命令后,按照下面流程开始执行
-
保存主节点信息 -
建立与master的socket 链接 -
发送 ping 命令 -
权限校验(如果master 配置了requirepass) -
数据同步(这里因为是第一次,所以是全量复制,redis 2.8+后在某些场景下支持部分复制) -
命令持续复制
数据同步
redis 2.8 之前支持全量复制(sync命令),redis 2.8以及之后的版本支持全量复制和部分复制(psync [runId] [offsetId] 命令)
全量复制
全量复制指的是主节点会将全部数据一次性发给从节点,网络开销大。
部分复制
redis 2.8+支持,注意是用于解决主从复制过程时,由于网络闪断等原因操作数据丢失,在重连上后,可以继续对落后的数据进行补发,避免触发全量复制的大开销。
redis2.8+后的复制核心原理
从节点使用 psync 命令进行全量复制和部分复制,下面注意讲解一下 psync 命令,格式:psync [runId] [offset] ,参数含义如下:
-
runId: 主节点的运行id,发生重启时会生成不一样的id -
offset: 当前从节点已复制的数据偏移量
执行命令后,master可能返回3中情况:
-
+FULLRESYNC : 表示进行全量复制,哪些场景会触发这个:
-
初次复制,如命令:psync ? -1 -
runId 与该节点目前的id不一致 -
offset 在复制缓冲区找不到,可能缓冲区发生溢出(落后太久,主节点期间产生的命令在缓冲区占满,覆盖之前的命令),所以配置缓冲区大小时需要考虑这个方面,避免网络中断时缓冲区太小触发全量复制。
-
+CONTINUE: 表示进行部分复制 -
-ERR: 说明主节点版本低于2.8,不支持psync命令
如果收到+FULLRESYNC时,进行下面的全量复制流程:
-
从节点收到 +FULLRESYNC,保存master的信息,等待master发送RDB -
master 进行 bgsave 保存RDB文件到本地,同时接受新的写的命令发送到复制客户端缓冲区 -
master 将RDB文件发送给从节点 -
从节点接受RDB文件保存下来,然后清空所有老的数据,再进行加载 RDB 文件 -
从节点加载 RDB 文件成功后,如果当前节点开启 AOF 持久化,进行 bgrewriteaaof 操作 -
从节点加载完 RDB后,从节点通过心跳机制给主节点汇报 offset,然后主节点将复制客户端的缓冲区数据将落后的数据发送过来
如果收到+CONTINUE时,进行下面的部分复制流程:
主节点根据从节点 psync 命令的offset,然后从复制客户端缓冲区查询对应的数据,将offset之后的数据发送给客户端。
异步复制
主节点执行客户端的写命令后,直接响应客户端结果,然后异步将写命令同步给从节点。
心跳机制
主节点和从节点都有一套心跳机制。 主节点的心跳机制:主节点默认每隔10秒给从节点发送 ping 命令来判断从节点的存活和连接状态。 从节点的心跳机制:在主线程中每隔1秒给主节点发送 replconf ack {offset} 命令来上报自身的复制偏移量,主节点也是根据这个上报信息来判断节点超时进行下线条件之一。
其他注意事项
从节点建议默认配置只读:
默认的配置为:slave-read-only=true,避免从节点被修改,造成主从节点不一致
读写分离注意事项
-
由于主从有延迟,读从库时,可能读到过期的数据。有以下场景:
-
在 redis 3.2 之前,读从库时,发现key已经过期,还是会返回,这个问题在 3.2 版本开始修复,跟主节点的惰性删除一样,判断key过期时,返回null -
expire key seconds 命令 + 主从延迟导致:主节点在执行 expire key seconds 时是以当前时间+ expire second 来设置最终的过期时间,当这个key延迟10秒同步到从节点,那么这个key在从节点的过期时间推迟了10秒,所以可能多的这10秒,期间有其他客户端访问到认为不过期。解决方法key通过 expireat 命令设置固定的时间点,但是要求个节点的机器时钟必须同步,否则也会存在同样问题 -
从节点故障,客户端需要切换到主节点或者其他从节点,客户端需要改造
总结:在工作量,其实用到读写分离比较少,主要是从节点故障的问题,一般直接上 redis cluster 或 codis 集群方案,不仅可以保证高可用,故障自动转移,还可以水平扩展存储容量以及读写性能。
参考资料
-
《redis 开发与运维》书籍
作者介绍

java后端领域
研发过亿日活的app,java 8年+