多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
`master-slave`集群的关键在于数据的同步,而在数据同步之前必须先建立连接,建立连接之后再进行数据同步。 #### 建立连接 建立连接主要分为以下几步: 1. 执行`replicaof`命令时,从服务器会在本地将主服务器的一些信息(如:`ip`和端口等信息)保存在`redisServer`内。 2. 创建和主服务器的连接,创建连接之后,从服务器就相当于主服务器的一个客户端。 3. 从服务器向主服务发送`ping`命令,确认连接是否可用,如果`Redis`服务器需要授权,这一步还会进行授权认证。 4. 如果从服务器收到主服务器的`pong`回复之后,表示当前连接是可用的。此时从服务器会将自己服务器的端口号发送给主服务器,主服务器收到之后将其记录在`redisClient`内。 5. 如果从服务器没收到主服务器返回`pong`,则会重新尝试建立连接。 注意:建立完连接之后,主从服务器会定时(间隔`1s`)向对方发送`replconf of`命令来检测对方是否正常。上图中在主服务器查看从服务器信息中有一个`lag`属性记录的就是上一次发送心跳包时间。 #### 同步 `master`和`slave`服务之间连接建立之后,就会开始进行数据同步,而**首次同步**一般用于首次建立主从连接的时候,这时候因为是初次建立`master-slave`关系,所以需要进行数据的全量同步。 首次数据全量同步主要分为以下步骤: 1. 从服务器向主服务器发送同步数据命令。 2. 主服务器接收到同步数据命令之后,则会执行`bgsave`命令,在后台生成一个`RDB`文件,并将其发送给从服务器,此时如果有新的命令过来,主服务器会将其记录在缓冲区内。 3. 从服务器收到主服务器发送过来的`RDB`文件之后,会首先清除自己的数据,然后载入`RDB`文件来生成数据。 4. 当从服务器执行完`RDB`文件之后,主服务器会将缓冲区的命令发送给从服务器,从服务器再依次执行。 #### 命令传播 执行完首次全量同步之后,这时候主服务器会将自己接收到的改变了数据库状态的命令发送给从服务器,从而使得主从服务器数据始终保持一致。 既然是命令传播,那么就不可避免的会造成数据延迟,`Redis`当中提供了一个参数来进行优化。 ~~~ repl-disable-tcp-nodelay no #默认是no ~~~ 当参数设置为`yes`时,`Redis`会将数据包进行合并发送,也就是降低了发送频率(发送频率与`Linux`内核配置有关);当参数设置为`no`时,则主服务器每执行一个能改变数据库状态的命令就会立刻实时同步给从服务器。 #### 部分重同步 上面就是在服务器正常情况下的同步措施,**数据全量同步 + 命令传播,可以使得正常情况下主从服务器数据保持一致**。然而如果说从服务器因为停电等其它因素导致其和主服务器之间的连接中途断开,那么当连接再次恢复正常之后,如果还是重新全量同步则效率会非常低,也显得没有必要,所以`Redis`还需要支持部分重同步。 **实现部分重同步最关键的地方就是需要记录原先同步的偏移量,只有这样才能在连接恢复正常之后继续实现命令传播,而无需传输整个`RDB`文件**。 ##### 同步偏移量 主从服务器各自都会维护一个数据复制的偏移量,这个偏移量表示的是发送命令的字节数。下图就是一个刚建立连接的主从服务器,默认偏移量`offset`都是`0`: ![](https://img.kancloud.cn/3d/17/3d176597826973f2a439d5a46b8e7490_446x274.png) 当主服务器向从服务器发送`100`字节之后,主从服务器的偏移量就会变成`100`。 ![](https://img.kancloud.cn/a2/e8/a2e8a14153407a10a58a276b40eb0ba7_495x262.png) 那么如果`master`再次向`slave1`和`slave2`传输了一个`200`字节的命令,`slave1`接收到了,而`slave2`没有接收到,就会出现以下情况: ![](https://img.kancloud.cn/85/4e/854ec2d4602efdf6a14791f590b1a238_457x267.png) 这时候当`slave2`再次和`master`恢复连接之后,此时`slave2`服务器会向主服务器发送同步命令,同步命令会带上偏移量。主服务器收到之后发现`slave2`发送过来的偏移量是`100`,而自己已经到`300`了,那么主服务器就会把`101`到`300`之间的命令再次进行发送给`slave2`,从而达到了部分重同步的目的。 ##### 复制积压缓冲区 上面的部分重同步貌似看起来能解决问题,但是这又会带来另一个问题,那就是当主服务器将命令发送出去之后,**为了实现部分重同步还需要将命令保存起来,否则当从服务器的偏移量低于主服务器时,主服务器也无法将命令重传播。** 现在问题就是,这个命令要保存多久呢?这个也不可能一直保存下去,因为如果一直保存下去就会占据大量的空间。所以为了解决这个问题,`master`服务器维护了一个固定长度的`FIFO`队列,称之为**复制积压缓冲区**。 当进行命令传播的过程中,`master`服务器不仅会将命令传播给所有的`slave`服务器,同时还会将命令写入复制积压缓冲区。复制积压缓冲区默认大小为`1MB`。 当`master`服务器发现`slave`服务器传送过来的复制偏移量 +1 已经不在复制积压缓冲区的时候,就会执行一次全量同步,即发送`RDB`文件给从服务器,从服务器收到之后就会清空当前所有数据,然后再从主服务器发送过来的`RDB`文件中进行数据恢复。 下面就是一个完整的部分重同步流程图: ![](https://img.kancloud.cn/7f/55/7f557ae5defee9e72800129c56a57ba0_933x320.png)