本文将分享关于redis主从复制的实现原理。主从复制在操作上很简单,
但大家未必了解它的底层实现,下面就分享一下redis是如何实现主从
复制的。
引言
主从复制有两种方式,分别为同步和命令传播。在从服务器上通过命令slave of host port来实现从主服务器上同步数据。
老版本主从复制
主从复制分为两步,同步(SYNC)和命令传播:
上图是主从复制的流程:
- 首先从服务器发起同步请求给主服务器,主服务器接到请求后,将在后台执行bgsave生成rdb持久化文件,在此过程中接收到客户端的请求会将命令存放到缓冲区中。
- 主服务器生成rdb持久化文件完成后,将文件发送给从服务器,从服务器执行rdb文件后,将返回给主服务器一个响应。
- 主服务器接收到从服务器发过来的响应后,将缓冲区中的所有命令发送给从服务器。
- 从服务器接收到主服务器发来的命令后,执行这些命令,即完成了一次复制的过程。
但是这个模式是有问题的,如果从服务器在命令传播的时候与主服务器断开了连接,之后又重新发起了连接,然后在进行主从复制操作,但此时,主服务器不知道从服务器复制到哪个命令了,无法重新进行命令传播,只能从新全量同步目前主服务器上的数据,这样就造成了不必要的IO。为了解决这个问题,在新版本的redis中,通过PSYNC同步操作来实现全量复制和部分复制。
新版本主从复制
在新版本中,主从复制的流程和老版本大致一致,只是在主服务器执行bgsave的时候,如果此时客户端发来请求,服务器会将请求命令存入到一个叫做复制积压缓冲区中,这个复制积压缓冲区大小默认为1M,数据结构为一个链表,记录着当前命令的偏移量和当前命令。当从服务器执行完bgsave的时候,主服务器会将复制积压缓冲区中的所有命令发给从服务器,并记录发送的最后一个命令的偏移量,当从服务器接受到指令后,记录主服务器发来的最后一个偏移量并进行执行命令,如果此时主从服务器断开连接,那么在从新连接上后,从服务器再次发起复制请求后,会将之前从服务器处理完的最后一个命令的偏移量发送给主服务器,主服务器就会在复制积压缓冲区中找到对应的偏移量所属的命令,从这个命令开始到复制积压缓存区中最后的命令全部发送给从服务器,也叫做部分复制;如果从服务器给主服务器的偏移量已经不在复制积压缓存区了,那么就会进行全量复制,也就是主服务器在后台执行bgsave操作,然后发给从服务器。这样就不会每次断开连接,再重新连接上后都做一次全量复制,避免了不必要的开销。
心跳检测
从服务器每一秒中会向主服务器发送一个REPLCONF ACK 指令,来进行心跳检测。其中是从服务器的复制偏移量。心跳检测有三个作用:
- 检测主从服务器网络连接状态。当主服务器超过1秒没有接受到从服务器的心跳请求后,那么主服务器就知道该从服务器与自己的连接出现了故障。
- 检测min-slave配置项。当在主服务器上配置
min-slave-to-write=3
min-slave-max-lag=10
也就是如果主服务器的从服务器数量少于3个或者存在从服务器超过10秒没有与主服务器进行心跳请求,那么此时主服务器就拒绝接收客户端发来的写命令。 - 检测命令丢失。通过参数可以确定命令是否丢失。在进行命令传播的过程中,主服务器发送给从服务器命令后会记录复制偏移量,然后将命令发送给从服务器。从服务器执行后也会记录复制偏移量。offset就是从服务器执行的最后一个命令的偏移量。如果主服务当前的复制偏移量为200,但是从服务器发来的是198,那么199和200这两个命令就是在传输过程中丢失了,此时主服务器会将199和200这两个命令再次发送给从服务器。
最后
如果本文对你有价值,哪怕一点点,请记得关注、点赞,有不足或疑问之处请留言。