redis主从复制
redis的一主多从能很好地提升性能,但这种系统面临一个很大的一个同步问题,特别是在系统重启,链接断开的情况下,如何保持主从数据一致是一个很重要的事情。
本文将从redis不同版本来对redis为保证主从数据库数据一致性而做的主从复制做详细说明。
主从复制步骤
从服务器从敲入复制命令到主从完成复制过程可以分为以下几个步骤:
1.客户端向从服务器发送SLAVEOF -h -p ,从服务器给客户端返回ok,此时主从复制真正开始
2.从服务器和主服务器之间建立连接,此时我们可以将从服务器看做主服务器的客户端,因为之后从服务器需要向主服务器发送各种命令
3.首先发送PING命令,主服务器返回PONG命令则表示主从之间通信正常,否则从服务器会尝试重连
4.从服务器收到PONG命令后,如果从服务器设置了masterauth选项,那么进行身份验证,此时从服务器向主服务器发送AUT命令来和主服务器进行权限验证。
5.从服务器向主服务器发送监听端口,执行命令REPLCONF listening-port <port number>
6.从服务器发送PSYNC命令(redis2.8之前是SYNC命令)开始同步数据
7.建立复制流程后,主从服务器就会进入命令传播阶段,主服务器会一直将自己执行的写命令发送给从服务器
以上就是主从复制的大致步骤
SYNC命令
2.8版本之前使用SYNC命令来进行复制,包括两个操作:
- 同步(sync)
- 命令传播(command propagate)
同步步骤:
1.从服务器向主服务器发送SYNC命令
2.收到SYNC命令后执行BGSAVE命令,后台生成RDB文件,并且使用缓冲区来保存从现在开始执行的所有命令
3.主服务器BGSAVE命令执行完后,把RDB文件发送从服务器,从服务器载入文件进行同步
4.主服务器将缓冲区内命令发送给从服务器,从服务器执行最终达成主从数据一致
上述过程看起来很ok了,但是存在缺陷问题,倘若在第4步时主从之间失去联系,在重新连上后,主从之间会再次走完这四步,但是明显只有第4步是必须的,我们知道生成RDB文件设计到IO操作,这会是一个耗时操作,明显这一步需要优化。
PSYNC命令
2.8之后,redis使用PSYNC命令来代替SYNC命令,该命令具有两种模式
- 完整重同步:类似于SYNC命令的步骤,写RDB,执行缓冲区保存掉的命令
- 部分重同步:处理掉线后的情况,此时主服务器只需要在掉线重连后将缓冲区的命令发给从服务器即可,而不需要再来一遍完整同步。
部分重同步
部分重同步由以下三个功能组成:
主从服务器的复制偏移量
主从服务器各自维护一个复制偏移量,这个偏移量的起的作用就是如果主从处于一致性,则相等,否则不相等主服务器的复制积压缓冲区
这是主服务器的一个固定长度的先进先出(FIFO)队列,默认大小是1MB,命令传播时,主服务器不仅将命令传播给从服务器,也会写入这个缓冲区,此时在发生断开连接再重现连接的情况时,主服务器就根据复制偏移量和缓冲区来决定是部分重同步还是完整重同步(因为服务器并不知道重新连接后到底是因为故障而重新连接,还是正常的初始连接)
以下是缓冲区的构造:
如果偏移量之后的数据(也就是偏移量+1开始的数据)还在这个缓冲区内,此时进行部分重同步,否则完整重同步。
- 服务器的运行ID
这是一个由40个随机的十六进制组成的字符,断线重连或者初次连接时从服务器会将这个ID发给主服务器,如果这个两个ID同步,则表示本次是断线重连而不是初次连接,此时执行部分重同步,否则完整重同步。
PSYNC2
Redis 4.0 版本新增 混合持久化,还优化了 psync(以下称 psync2,实现即使redis实例重启的情况下也能实现部分同步,PSYNC2在之前的PSYNC基础上新增两个复制id
- master_replid: 复制id1(后文简称:replid1),一个长度为41个字节(40个随机串+’0’)的字符串,每个redis实例都有,和runid没有直接关联,但和runid生成规则相同。当实例变为从实例后,自己的replid1会被主实例的replid1覆盖。
- master_replid2:复制id2(后文简称:replid2),默认初始化为全0,用于存储上次主实例的replid1。
在4.0之前的版本,redis复制信息完全丢失,所以每个实例重启后只能进行全量复制,到了4.0版本,任然可以使用部分同步,其实现过程
1.存储复制信息
redis在关闭时,通过shutdown save,都会调用rdbSaveInfoAuxFields函数,把当前实例的repl-id和repl-offset保存到RDB文件中,当前的RDB存储的数据内容和复制信息是一致性的可通过redis-check-rdb命令查看。
2.重启后加载RDB文件中的复制信息
redis加载RDB文件,会专门处理文件中辅助字段(AUX fields)信息,把其中repl_id和repl_offset加载到实例中,分别赋给master_replid和master_repl_offset两个变量值,特别注意当从库开启了AOF持久化,redis加载顺序发生变化优先加载AOF文件,但是由于aof文件中没有复制信息,所以导致重启后从实例依旧使用全量复制!
3.向主库上报复制信息,判断是否进行部分同步
从实例向主库上报master_replid和master_repl_offset+1;从实例同时满足以下两条件,就可以部分重新同步,否则执行全量同步:
- 从实例上报master_replid串,与主实例的master_replid1或replid2有一个相等,用于判断主从未发生改变;
- 从实例上报的master_repl_offset+1字节,还存在于主实例的复制积压缓冲区中,用于判断从库丢失部分是否在复制缓冲区中;
PSYNC2除了解决redis重启使用部分同步外,还为解决在主库故障时候从库切换为主库时候使用部分同步机制。redis从库默认开启复制积压缓冲区功能,以便从库故障切换变化master后,其他落后该从库可以从缓冲区中获取缺少的命令。该过程的实现通过两组replid、offset替换原来的master runid和offset变量实现:
- master_replid和master_repl_offset:如果redis是主实例,则表示为自己的replid和复制偏移量; 如果redis是从实例,则表示为自己主实例的replid1和同步主实例的复制偏移量。
- master_replid2和second_repl_offset:无论主从,都表示自己上次主实例repid1和复制偏移量;用于兄弟实例或级联复制,主库故障切换psync。
判断是否使用部分复制条件:如果从库提供的master_replid与master的replid不同,且与master的replid2不同,或同步速度快于master; 就必须进行全量复制,否则执行部分复制。
以上便是redis主从复制的全部内容。
参考资料
- <<Redis设计与实现>>
-
深入理解redis主从复制原理
-
Redis 主从复制 psync1 和 psync2 的区别