暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

主从复制原理

程序猿研习社 2021-09-06
148


  • 概念

  • 旧版本主从复制

  • 新版本主从复制

  • 部分同步的实现原理



01

概念


在redis中,用户可以通过slaveof命令让一台服务器去复制另一台服务器的数据,发起复制的叫从服务器,被复制的交主服务器,进行数据复制的主从服务器将会保持数据的一致性;


02


旧版本主从复制


两步:数据同步+命令传播

数据同步

目的:将从服务器的数据库状态更新至主服务器的所处的数据库状态;

当客户端向从服务器发送slaveof命令后,要求从服务器复制主服务器时,会经历四步完成数据同步:

  1. 从服务器向主服务器发送sync命令

  2. 收到sync命令的主服务器执行bgsave命令,在后台生成一个rdb文件,并使用一个缓冲区记录从现在开始执行的所有写命令

  3. 当主服务器的bgsave命令执行完毕后,会通过主从服务器之间的socket连接,将rdb文件发送给从服务器,从服务器接收并载入rdb文件完成数据库状态的同步

  4. 最后,主服务器将记录在缓冲区里的写命令发送给从服务器,从服务器接收并执行这些写命令,从而完成最终的数据同步 (完整过程如下图所示)



命令传播

目的:在主服务器数据库状态被修改,导致从服务器的数据库状态和主服务器不一致时,让主从服务器数据库重新回到一致

举个例子:假如一个主服务器和一个从服务器刚刚完成了数据同步,它们的数据库都保存了k1-k4四个键,如下图所示:

此时客户端发起了del请求,将k1删除,此时主从服务器的数据库将处于不一致的状态,如下图所示:

现在由于客户端将主服务器的k1删除,导致主从数据库状态的不一致,那怎么办?这很容易想出来吧各位同学,让主服务器将del命令发送给从服务器就ok啦!如下图所示:


主从复制完整过程如下表格所表示:

时间
主服务器从服务器
T0
启动
启动
T1
执行set k1 v1

T2
执行set k2 v2

T3
执行set k3 v3

T4

向主服务器发送sync
T5
收到sync,执行bgsave命令生成rdb文件,并使用缓冲区记录从当前时间开始的写命令

T6
执行set k4 v4 并将命令保存到缓冲区

T7
执行set k5 v5 并将命令保存到缓冲区

T8
bgsave执行完毕,向从服务器发送rdb文件

T9

接收rdb文件,获得k1,k2,k3三个键
T10
向从服务器发送缓冲区里的k4和k5写命令

T11

接收到set k4 v4和setkey5 v5两个命令
T12
同步完成同步完成,数据为k1到k5


旧版本复制的缺点

从服务器如果断线重连时,会同步主服务器的全量数据,而不是同步断开过程中产生的主从数据差


03


新版本主从复制


为了解决旧版复制功能在处理断线重连时的低效问题,redis从2.8版本开始,使用psync代替sync来执行主从复制的操作,psync有两种模式,一个是完整同步,另一个是部分同步;


完整同步

用于处理初次同步,和旧版的sync一个意思,也是通过主服务器发送rdb文件以及缓冲区里的写命令来完成同步;


部分同步:

用于处理从服务器断线重连的情况,主服务器可以将从服务器断开连接期间的写命令发送给从服务器,这样就完成部分同步,而不需要像老版本那样同步全量数据;


使用psync命令进行断线重连后的部分同步过程如下表格:

时间
主服务器从服务器
T0
主从同步完成主从同步完成
T1
执行set k1 v1
接收主发来的set命令并执行
T2
执行set k2 v2
接收主发来的set命令并执行
T3
执行set k3 v3
接收主发来的set命令并执行
T4
主从连接断开
主从连接断开
T5
执行set k4 v4断线中,尝试连接主服务器
T6
执行set k5 v5 
断线中,尝试连接主服务器
T7
主从连接成功主从连接成功
T8

发送psync命令
T9
向从服务器返回+continue恢复,表示执行部分同步

T10

接收continue回复,准备部分同步
T11
向从服务器发送set k4和set k5

T12

接收到主服务器发送的set k4和set k5并执行
T13
同步完成同步完成


04


部分同步的实现


部分同步由三个部分组成

  • 主服务器的复制偏移量和从服务器的复制偏移量

  • 主服务器的复制缓冲区

  • 服务器的运行id(runid)


复制偏移量

执行复制的双方都会维护一个复制偏移量

  • 主服务器每次向从服务器发送N个字节的数据时,会将自己的复制偏移量加N

  • 从服务器接收到主服务器发送过来的N个字节数据后,也会将自己的复制偏移量加N


如下图所示,主从偏移量都是10086


这时,主服务器向三个从服务器发送了33字节的数据,那么主服务器的偏移量会变成10086+33=10119,而从服务器在收到数据后也会将偏移量设置为10119,如下图所示:


考虑下面的场景,在主服务器发送33字节给从服务器时,主从连接突然断开,如下图所示:


那么在从1与主服务器重连之后,数据该如何同步呢?按照我们的猜想,肯定是根据主从的复制偏移量来进行部分同步,没错,你猜对了😘,不过还与复制缓冲区有关(毕竟至少要告诉主服务器从offset开始,发送多少个字节数据给从服务器吧)


复制缓冲区

由主服务器维护的一个固定长度的先进先出(FIFO)队列,默认大小为1MB

当主服务器发送写命令给从服务器时,同时也会将写命令入队到复制缓冲区中,如下图所示:


因此,主服务器的复制缓冲区内保存了最近发送的写命令,并且会记录每个字节对应的偏移量,如下表格所示:

当从服务器重连上主服务器后,从服务器会通过psync命令将自己的offset发送给主服务器,主服务器会根据这个偏移量来决定做何种操作:

  • 如果offset之后的数据仍然存在于复制缓冲区,那么执行部分同步操作

  • 如果offset之后的数据不存在,那么执行完整同步操作


回到上面主从断开连接的图,当主从服务器恢复连接后

  1. 从服务器通过psync命令告诉主服务器自己的offset是10086

  2. 主服务器收到psync命令和offset之后,检查了一下复制缓冲区10086偏移量后面的数据还在不在,结果发现这些数据还在,就给从服务器返回了一个continue表示要进行部分同步

  3. 接着主服务器会将复制缓冲区里10086偏移量后面的数据(10087至10119)都发送给从服务器

  4. 从服务器只要接收到这33字节的数据,就可以回到与主服务器一致的状态,如下图所示:


服务器运行id(用来判断部分同步还是完整同步)

每个redis服务器无论主从都有自己的运行id,当从服务器首次同步的时候,主服务器会将运行id传给从服务器,从服务器将其保存,当从服务器断线重连时,从服务器会将之前的主服务器运行id和当前的比较一下:

  • 如果相同,说明断开和重连后连接的是同一台主服务器,那么进行部分同步就可以了

  • 相反,主服务器已经改变,那么进行完整同步




文章转载自程序猿研习社,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论