深入了解Redis中的主从同步机制
导读
一、Redis主从有两种结构模型:
1.1 主从复制
一主N从的这种复制结构复制关系只有一级,也是使用最多的形式,通常搭建哨兵或者集群结构的redis都是采用的这种复制结构,能够通过一级从节点的复制关系很好的保证服务的可用性,做到异常情况主从切换。
1.2 级联复制
级联复制结构的复制关系可以有多级,一个主节点的从节点可以是下属从节点的主节点。级联复制结构的应用相对比较少,这种结构能够在有多个从节点的结构下一定程度上缓解主节点的复制压力。
二、Redis主从关系的建立
redis的主从同步始于命令SLAVEOF host port,通过这个命令能够建立主从关系,SLAVEOF 命令用于在 Redis 运行时动态地修改复制功能的行为。通过执行 SLAVEOF host port 命令,可以将当前服务器转变为指定服务器的从属服务器(slave server)。如果当前服务器已经是某个主服务器(master server)的从属服务器,那么执行 SLAVEOF host port 将使当前服务器停止对旧主服务器的同步,丢弃旧数据集,转而开始对新主服务器进行同步。另外,对一个从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。利用 SLAVEOF NO ONE 不会丢弃同步所得数据集这个特性,在没有搭建哨兵和集群的情况下,可以在主服务器失败的时候,将从属服务器用作新的主服务器,从而实现无间断运行。
下图为主从关系建立流程:
注意:
根据上执行流程这里有一个需要注意点,当我们对一个已有主从关系的节点执行slaveof命令时,会结束掉现有的主从关系并清空节点下的所以数据,在生成环境当中这是比较威胁的操作。有没有更安全的方式了?上面介绍slavelof命令的时候提到可以传递NO ONE参数,也就是执行SLAVEOF NO ONE命令,这个命令是只会结束主从复制关系不会清空数据的,相对安全很多。
三、数据同步
建立好主从关系后就要进入主从数据同步的过程了,这里主要分三种情况,刚建立主从关系后的数据全量同步;初始化同步完成后的命令传播阶段;主从关系异常中断重连后的同步方式选择,这里会有全量和增量同步两种场景。
3.1 全量同步
-
当slave节点启动后或断开重连后(重连不满足增量同步条件),会向master数据库发送SYNC命令。
-
master节点收到SYNC命令后会开始在后台保存快照(即RDB持久化,在主从复制时,会无条件触发RDB),并将保存快照期间接收到的命令缓存起来。
-
master节点执行RDB持久化完成后,向所有slave节点发送快照RDB文件,并在发送快照期间继续记录被执行的写命令。
-
slave节点收到快照文件后丢弃所有旧数据(会清空所有数据),载入收到的快照。
-
master节点快照发送完毕、slave节点载入快照完毕后,master节点开始向slave节点发送缓冲区中的写命令。
-
slave节点完成对快照的载入,开始接收命令请求,并执行来自主数据库缓冲区的写命令。(从数据库初始化完成)
-
master节点每执行一个写命令就会向slave节点发送相同的写命令,slave节点接收并执行收到的写命令。(命令传播操作,slave节点初始化完成后的操作)
全量同步流程如下图:
在redis2.8之前,从节点无论是初始化还是断线重连后都是采用全量同步的方式,在2.8之后版本,引入PSYNC命令,在从节点断线重连后会判断是否采用增量同步。
3.2 增量同步
PSYNC具备了数据全量重同步和增量同步模式。
-
全量重同步:跟旧版复制基本是一致的,可以理解为“全量”复制。
-
部分重同步:salve断开又重新连时,在命令传播阶段,只需要发送与master断开这段时间执行的写命给slave即可,可以理解为“增量”复制。
PSYNC执行过程中比较重要的概念有3个:runid、offset(复制偏移量)以及复制积压缓冲区。
1.runid
每个Redis服务器都会有一个表明自己身份的ID。在PSYNC中发送的这个ID是指之前连接的Master的ID,如果没保存这个ID,PSYNC的命令会使用”PSYNC ? -1” 这种形式发送给Master,表示需要全量复制。
2.offset(复制偏移量)
在主从复制的Master和Slave双方都会各自维持一个offset。Master成功发送N个字节的命令后会将Master里的offset加上N,Slave在接收到N个字节命令后同样会将Slave里的offset增加N。Master和Slave如果状态是一致的那么它的的offset也应该是一致的。
3.复制积压缓冲区
复制积压缓冲区是由Master维护的一个固定长度环形积压队列(FIFO队列),它的作用是缓存已经传播出去的命令。当Master进行命令传播时,不仅将命令发送给所有Slave,还会将命令写入到复制积压缓冲区里面。PSYNC执行过程和SYNC的区别在于:salve连接时,判断是否需要全量同步,全量同步的逻辑过程和SYNC一样。PSYNC执行步骤如下:
-
客户端向服务器发送SLAVEOF命令,即salve向master发起连接请求时,slave根据自己是否保存Master runid来判断是否是第一次连接。
-
如果是第一次同步则向Master发送 PSYNC ? -1 命令来进行完整同步;如果是重连接,会向Master发送PSYNC runid offset命令(runid是master的身份ID,offset是从节点同步命令的全局迁移量)。
-
Master接收到PSYNC 命令后,首先判断runid是否和本机的id一致,如果一致则会再次判断offset偏移量和本机的偏移量相差有没有超过复制积压缓冲区大小,如果没有那么就给Slave发送CONTINUE,此时Slave只需要等待Master传回失去连接期间丢失的命令。如果runid和本机id不一致或者offset差距超过了复制积压缓冲区大小,那么就会返回FULLRESYNC runid offset,Slave将runid保存起来,并进行全量同步。
主节点在命令传播时,主数据库会将每一个写命令传递给从数据库的同时,都会将写命令存放到积压队列,并记录当前积压队列中存放命令的全局偏移量offset。当salve重连接时,master会根据从节点传的offset在环形积压队列中找到断开这段时间执行的命令,并同步给salve节点,达到增量同步结果。
PSYNC执行流程如下图:
从以上PSYNC的执行流程可以看出当slave节点断线重连以后判断是否采用增量同步的核心是slave的offset偏移量和master的偏移量相差有没有超过复制积压缓冲区大小,那么这个大小是由以下参数来配置的。复制积压缓冲区本质上是一个固定长度的循环队列,默认情况下积压队列的大小为1MB,可以通过配置文件设置队列大小:设置复制积压缓冲区大小,积压队列越大,允许主从数据库断线的时间就越长
repl-backlog-size 1mb
Redis同时也提供了当没有slave需要同步的时候,多久可以释放环形队列,默认一小时,没有salve连接时,多久释放一次复制积压缓冲区
repl-backlog-ttl 3600
四、主从复制策略
Redis采用了乐观复制的策略,也就是在一定程度内容忍主从数据库的内容不一致,但是保持主从数据库数据的最终一致性。具体来说,Redis在主从复制的过程中,本身就是异步的,在主从数据库执行完客户端请求后会立即将结果返回给客户端,并异步的将命令同步给从数据库,但是这里并不会等待从数据库完全同步之后,再返回客户端。这一特性虽然保证了主从复制期间性能不受影响,但是也会产生一个数据不一致的时间窗口,如果在这个时间窗口期间网络突然断开连接,就会导致两者数据不一致。如果不在配置文件中添加其他策略,那就默认会采用这种方式。为了防止主从不一致不可控,redis提供了以下两个参数来做约束:
min-slaves-to-write 3 min-slaves-max-lag 10
当slave数量小于min-slaves-to-write,且延迟小于等于min-slaves-max-lag时,master停止写入操作。
还有一个参数也会影响主从之间的延时:
repl-disable-tcp-nodelay:
设置成yes,则redis会合并小的TCP包从而节省带宽,但会增加同步延迟,造成master与slave数据不一致。设置成no,则redis master会立即发送同步数据,几乎没有延迟。
Redis的主从同步无论那种场景可以抽象为以下七个步骤:
1.建立socket连接
从服务器根据设置的套接字创建连向主服务器的套接字连接,主服务器接收从服务器的套接字连接之后,为该套接字创建响应的客户端状态,并将此时的从服务器看做是主服务器的客户端,也就是该从服务器同时具备服务器与客户端两个身份。
2.发送PING命令
PING命令主要有两种作用:虽然建立了套接字连接,但是还未使用过,通过发送PING命令检查套接字的读写状态是否正常;通过发送PING命令检查主服务器能否正常处理命令请求,能处理主服务器回复PONG。
3.身份验证
从服务器接收到主服务器返回的“PONG”回复,接下来就需要考虑身份验证的事。如果从服务器设置了masterauth选项,那么进行身份验证,如果从服务器没有设置masterauth选项,那么不进行身份验证。
4.发送端口信息
在身份验证步骤之后,从服务器将执行命令REPLCONF listening-port ,向主服务器发送从服务器的监听端口号。
5.数据同步
从服务器向主服务器发送SYNC命令、PSYNC命令,执行同步操作。
6.命令传播
主从服务器就会进入命令传播阶段,主服务器只要将自己执行的写命令发送给从服务器,而从服务器只要一直执行并接收主服务器发来的写命令。
五、告一段落
本篇详细介绍了redis主从同步机制,不同场景下同步策略的选择,这也是redis高可用的基石。在此基础上,下一篇将对redis高可用的实现来进行分析讲解。