主从复制
辅助链接 Redis 设计与实现:主从复制 | 郭宁的个人博客
Redis中,用户用户可以通过执行 SLAVEOF
命令或者设置 slaveof
选项,让一个服务器去复制(replicate)另一个服务器,我们称之为主从复制。被复制的服务器为主服务器(Master),进行复制的服务器为从服务器(slaver),进行复制中的主从服务器双方的数据库将保存相同的数据。
-
旧版复制
Redis的复制功能分为同步
sync
和命令传播command propagate
两个操作。从服务器对主服务器的同步操作需要通过向主服务器发送
sync
命令来完成。- 从服务器向主服务器发送
sync
命令。 - 收到
sync
命令的主服务器执行bgsave
命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。 - 当主服务器的
bgsave
命令执行完毕时,主服务器会将生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行bgsave
命令时的数据库状态。 - 主服务器将记录在复制缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
- 后续当主服务器发生修改的时候,主服务器需要对从服务器执行
command propagate
操作:主服务器会将自己执行的写命令,然后将这条写命令发送给从服务器执行,从服务器执行后,主从服务器的数据恢复到一致的状态。
- 从服务器向主服务器发送
-
新版复制
旧版的复制在处理断线重复制情况时,效率很低,需要全部重新复制,因此Redis采用了
psync
命令来代替了sync
命令。psync
命令具有完整重同步full resynchronization
和部分重同步partial resynchronization
两种模式:full resynchronization
的执行步骤和sync
执行复制的过程一样。partial resynchronization
则用于处理断线后重复制的情况。主从服务器都会分别维持一个复制偏移量来表示各自数据的offset,当主从服务器数据一致的时候,彼此的offset应该相同。同时,主服务器还会维持一个固定长度的复制积压缓冲区(FIFO),主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,同时记录相应的复制偏移量。当从服务器短线重连后,若从服务器offset之后的数据在缓冲区内,则执行partial resynchronization
,由主服务器向从服务器发送断线期间主服务器执行的写命令,否则执行full resynchronization
。- 复制积压缓冲区:master 进行命令传播时,会将命令发送给所有 slave,同时,写入复制积压缓冲区并记录复制偏移量。由于复制积压缓冲区的大小有限,后来的写命令进入到队列中会导致前面的写命令出列,因此复制积压缓冲区仅仅保存的是最近传播的一部分写命令。
-
心跳检测
在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令
REPLCONF ACK <replication_offset> // offset 表示复制迁移量
心跳检测主要用来检测主从服务器的网络状态,辅助实现min-slaves选项 和 检测命令丢失。
- 检测命令丢失:slave 的复制偏移量,小于 master 的复制偏移量,则master 会从 复制积压缓冲区中,取出丢失的命令,再次发给 slave 执行;上述补发数据,跟网络断线重新连接过程很相似,唯一差异:当前只是丢失命令,并 master, slaver 并没有断开网络连接,没有触发 断开网络连接的机制。
哨兵
辅助链接 Redis 设计与实现:Sentinel | 郭宁的个人博客
Sentinel
哨兵是Redis的高可用性的解决方案:由一个或多个Sentinel
实例组成的Sentinel
系统,可以监控多个 master
服务器及其下属 slave
服务器。在master
下线后,Sentinel
将在其下属的slave
中选举出一个 new master
服务器,并将 old master
设置为new master
的slave
。 当old master
重连后,将作为 slave
存在。
- 初始化服务器:
Sentinel
在本质上是一个以特殊模式运行的Redis服务器,它并不使用数据库,而是去监控master和slave的状态。在启动Sentinel
服务器的过程中,需要将一部分普通Redis服务器的代码替换成Sentinel
专用代码。 - **初始化Sentinel状态:**应用一个
Sentinel
专用的代码后,创建一个sentinelState
的结构来保存Sentinel
的状态。sentinelState
中的master属性保存了所有被Sentinel
监控的主服务器的相关信息。 - 建立相关的网络连接:
Sentinel
会与被监控的主服务器建立两个网络连接:命令连接和订阅连接。- 命令连接专门用于向主服务器发送命令,并接收命令回复
- 订阅连接用于订阅主服务器的
_**sentinel_**:hello频道
。
- 获取
master
,slave
信息:Sentinel
默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO
命令,并通过分析INFO
命令的回复来获取主服务器的当前信息。通过获取的master
信息可以得到其下属的slave
信息。Sentinel
与每一个slave
建立命令连接和订阅连接,且以相同的模式每十秒向被监视的slave
发送INFO
命令来获取状态信息。 - 向被监视的服务器发送信息:
Sentinel
会以每两秒一次的频率,通过向被监视服务器的_**sentinel_**:hello频道
发送消息来向其他Sentinel
宣告自己的存在。
-
sentinel系统流程
- sentinel 启动时,指定其监听的 master(可以指定多个 master)
- sentinel 与 master 建立「命令连接」
- sentinel 通过与 master 建立的「命令连接」,发现 slave
- sentinel 与 slave 建立「命令连接」
- sentinel 通过与 master 建立的「命令连接」,建立「订阅连接」
- sentinel 通过「订阅连接」发现其他 sentinel
- sentinel 与其他 sentinel 建立 「命令连接」,形成 sentinel 系统
-
几个连接的作用
- sentinel 与 master 之间的「命令连接」:发现 slave
- sentinel 与 slave 之间的「命令连接」:old master 下线后,设置 new master,同时,将其余 slave 设置为 new master 的 slave
- sentinel 与 master 之间的「订阅连接」:发现同一个 master 上的其他 sentinel,形成 sentinel 系统
- sentinel 与 slave 之间的「订阅连接」:通过订阅连接来从频道中接收信息
- sentinel 与 sentinel 之间的「命令连接」:当某一个 old master 下线后,推举出一个领头的 sentinel,由这个 sentinel 负责进行故障迁移
-
主观下线
一般情况下,
sentinel
会以每秒一次的频率向所有与它创建了命令连接的实例发送一个PING
命令,并通过实例返回PING
命令的回复来判断实例是否在线。若收到的回复是无效回复(只有 +PONG、-LOADING、-MASTERDOWN 有效)或者在一定时限内没有收到回复,sentinel
会修改这个实例所对应的实例结构,在结构的flags
属性中打开SRI_S_DOWN
标识,以此来表示这个实例已经进入主观下线状态。 -
客观下线
sentinel
发现master
下线之后,向其他sentinel
询问master
是否下线,其他sentinel
会立刻通过「命令连接」进行判断,如果超过设定数量(quorum)的sentinel
都认为master
已经下线,则设置master
为「客观下线」,即,多个sentinel
认为的下线。 -
故障转移
当一个
master
被判定为客观下线时,监视这个master
的所有sentinel
会选举出来一个领头sentinel
,主导对这个master
的故障转移。选举领头
sentinel
的方式主要是先到先得:发现master
进入到客观下线的sentinel
会向其他的sentinel
发送SENTINEL is-master-down-by-addr
命令,要求其他sentinel
将自己设置为局部领头sentinel
。当一个sentinel
被半数以上的sentinel
设置为局部领头sentinel
,它就会被最终设置为领头sentinel
,主导故障转移。领头
sentinel
会按照优先级、复制偏移量等信息选举出来一个最优的新master
,并向他发送SLAVEOF no one
命令,将其升级。当新master
升级完成后,领头sentinel
向其他的slave
发送SLAVEOF
命令,将其他的slave
设置为新master
的slave
,最后将旧master
设置为新master
的slave
。