Redis如何高效可靠地实现主从复制?终于有人讲明白了

导读:本文内容主要包括redis主从复制功能的概述、作用和方案实施 。
作者:李乐
来源:华章科技
Redis如何高效可靠地实现主从复制?终于有人讲明白了

文章插图
 
Redis支持主从复制功能,用户可以通过执行slaveof命令或者在配置文件中设置slaveof选项来开启复制功能 。例如,现在有两台服务器—127.0.0.1:6379和127.0.0.1:7000,向服务器127.0.0.1:6379发送下面命令:
127.0.0.1:6379>slaveof 127.0.0.1 7000OK此时服务器127.0.0.1:6379会成为服务器127.0.0.1:7000的从服务器(slaver),服务器127.0.0.1:7000会成为服务器127.0.0.1:6379的主服务器(master);通过复制功能,从服务器127.0.0.1:6379的数据可以和主服务器127.0.0.1:7000的数据保持同步 。
为什么需要主从复制功能呢?
简单来说,主从复制功能主要有以下两点作用 。
  1. 读写分离,单台服务器能支撑的QPS是有上限的,我们可以部署一台主服务器、多台从服务器,主服务器只处理写请求,从服务器通过复制功能同步主服务器数据,只处理读请求,以此提升Redis服务能力;另外我们还可以通过复制功能来让主服务器免于执行持久化操作:只要关闭主服务器的持久化功能,然后由从服务器去执行持久化操作即可 。
  2. 数据容灾,任何服务器都有宕机的可能,我们同样可以通过主从复制功能提升Redis服务的可靠性;由于从服务器与主服务器数据保持同步,一旦主服务器宕机,可以立即将请求切换到从服务器,从而避免Redis服务中断 。
对于本例来说slaveof命令的主要流程如下 。
  1. 从服务器127.0.0.1:6379向主服务器127.0.0.1:7000发送sync命令,请求同步数据 。
  2. 主服务器127.0.0.1:7000接收到sync命令请求,开始执行bgsave命令持久化数据到RDB文件,并且在持久化数据期间会将所有新执行的写入命令都保存到一个缓冲区 。
  3. 当持久化数据执行完毕后,主服务器127.0.0.1:7000将该RDB文件发送给从服务器127.0.0.1:6379,从服务器接收该RDB文件,并将文件中的数据加载到内存 。
  4. 主服务器127.0.0.1:7000将缓冲区中的命令请求发送给从服务器127.0.0.1:6379 。
  5. 每当主服务器127.0.0.1:7000接收到写命令请求时,都会将该命令请求按照Redis协议格式发送给从服务器127.0.0.1:6379,从服务器接收并处理主服务器发送过来的命令请求 。
上述流程已经可以完成主从复制基本功能了,Redis 2.8以前就是这样实现的,但是注意到步骤2中存在持久化操作(bgsave),而这是一个非常耗费资源的操作 。
Redis如何高效可靠地实现主从复制?终于有人讲明白了

文章插图
 
举一个简单的例子 。
主服务器和从服务器之间是通过TCP长连接交互数据的,假设某个时刻主从服务器之间的网络连接发生故障且时间比较短,在此期间主服务器只执行了很少的写命令请求 。
待主从服务器之间的网络连接恢复后,从服务器会重新连接到主服务器,并发送sync命令请求同步数据 。这时候主服务器还需要执行持久化操作吗?显然是可以避免的,只要主服务器能够缓存连接故障期间执行的写命令即可 。
Redis 2.8提出了新的主从复制解决方案 。从服务器会记录已经从主服务器接收到的数据量(复制偏移量);而主服务器会维护一个复制缓冲区,记录自己已执行且待发送给从服务器的命令请求,同时还需要记录复制缓冲区第一个字节的复制偏移量 。从服务器请求同步主服务器的命令也改为了psync 。
当从服务器连接到主服务器时,会向主服务器发送psync命令请求同步数据,同时告诉主服务器自己已经接收到的复制偏移量,主服务器判断该复制偏移量是否还包含在复制缓冲区;如果包含,则不需要执行持久化操作,直接向从服务器发送复制缓冲区中命令请求即可,这称为部分重同步;如果不包含,则需要执行持久化操作,同时将所有新执行的写命令缓存在复制缓冲区中,并重置复制缓冲区第一个字节的复制偏移量,这称为完整重同步 。
详情可参照Redis源码,方法masterTryPartialResynchronization用于判断是否可以执行部分重同步;方法replicationFeedSlaves用于向所有从服务器广播命令 。
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc){ if (server.repl_backlog) { //将当前命令请求添加到复制缓冲区 } while((ln = listNext(&li))) { //向所有从服务器同步命令请求 }}另外,从服务器也会通过命令“REPLCONF ACK < reploff >”定时向主服务器汇报自己的复制偏移量;据此,主服务器一来可以检测从服务器是否有效,二来可以重新广播丢失的命令请求 。


推荐阅读