Redis设计 - 复制机制

2020-08-16  本文已影响0人  家硕先生

前言

Redis可以通过执行SLAVEOF命令或者配置slaveof选项,让一个服务器去复制(replicate)另一个服务器,被复制的叫主服务器(master),对主服务器进行复制的叫做从服务器(slave)。

如目前有Redis服务器:127.0.0.1:6379和127.0.0.1:6380
在127.0.0.1:6380执行命令:SLAVEOF 127.0.0.1 6379,那么127.0.0.1:6380这台服务器将成为127.0.0.1:6379这台的从服务器。

进行复制中的主从服务器双方的数据库将保存相同的数据,成为“ 数据库状态一致 ”。在Redis2.8之前使用旧版本的复制,2.8后使用新版本的复制功能。

复制功能的实现

1. 旧版复制功能的实现

Redis的复制功能分为同步(sync)命令传播(command propagate)两个操作。

1.1 同步

当客户端向从服务器发送SLAVEOF命令,让其对主服务器进行复制时,从服务器首先要执行对主服务器的同步操作,让数据库状态达到主服务器当前状态,通过SYNC命令完成:

主从SYNC命令通信
1.2 命令传播

完成同步操作后,主从只是保持了在主服务器收到SYNC命令时,两个数据库状态的一致,当主服务器接收到客户端的写命令导致数据被修改时,主从的数据库状态就不一致了。

所以,主服务器会将自己执行的写命令,发送给从服务器执行(命令传播),从服务器执行了相同的写命令后 ,主从再次达到一致。

1.3 旧版复制的缺陷

在Redis中,从服务器对主服务器的复制可以分为以下两种情况:

旧版复制的弊端正是在于断线后重新复制,每次的断线都会导致全盘复制,其实从服务器需要同步的有时就差1、2个命令,这样的做法时低效且耗费资源的。

SYNC命令是个十分耗费资源的操作:
1)主服务器需要执行BGSVAE命令来生成RDB文件,这个操作会耗费主服务器大量的CPU、内存和磁盘I/O资源。
2)需要将RDB文件传输给从服务器,需要耗费主从服务器大量的带宽和流量,还会影响主服务器对命令的响应时间。
3)接收到RDB文件后,从服务器要对其进行载入,从服务器在载入期间阻塞没法处理命令请求。

2. 新版复制功能的实现

为了解决旧版本的复制功能在断线重复制情况下的低效问题,Redis从2.8版本开始,使用了PSYNC命令代替SYNC命令。

PSYNC具有 完整重同步(full resynchronization 和 部分重同步(partial resynchronization) 两种模式:

部分重同步过程
2.1 部分重同步的实现

部分重同步由三个部分构成:

2.1.1 复制偏移量
执行复制的双方,主服务器和从服务器分别维护一个复制偏移量:

主从服务偏移量同步示例如下:

一开始主从的偏移量都为10086

image.png

之后主服务器向从服务器传播长度33字节的数据

image.png

通过对比主从服务器的复制偏移量,程序便能知道主从服务器是否处于一致的状态。

2.1.2 复制积压缓冲区

问:如果从服务器断线后,复制偏移量落后于主服务器,那么主服务器是如何补偿从服务器断线期间丢失的那部分数据呢?
答:和主服务器的复制积压缓冲区有关。

复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将命令写入到复制积压缓冲区中。

主服务器向复制积压缓冲区写数据

主服务器中还保存着一部分最近执行的命令,并且复制缓冲区会为队列中的每个字节记录相应的复制偏移量,如下图所示:

复制积压缓冲区构造

当从服务器重新连接上主服务器时,从服务器通过PSYNC命令将自身的offset发送给主服务器,主服务器根据offset来决定下一步的操作:

关于积压缓冲区大小设置
Redis为复制积压缓冲区设置的默认大小为1MB,如果主服务器需要执行大量写命令,又或者主从服务器断线后重连接所需的时间比较长,那么这个大小也许并不合适。如果复制积压缓冲区的大小设置得不恰当,那么PSYNC命令的复制重同步模式就不能正常发挥作用,因此,正确估算和设置复制积压缓冲区的大小非常重要。
复制积压缓冲区的最小大小可以根据公式second * write_size_per_second来估算:

例如,如果主服务器平均每秒产生1 MB的写数据,而从服务器断线之后平均要5秒才能重新连接上主服务器,那么复制积压缓冲区的大小就不能低于5MB。为了安全起见,可以将复制积压缓冲区的大小设为2 * second * write_size_per_second,这样可以保证绝大部分断线情况都能用部分重同步来处理。

2.1.3 服务器运行ID

除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID:

主服务器收到的ID和自身相同,那么说明复制的目标就是当前的主服务器
如果不同,说明从服务器之前复制的并不是当前这个主服务器,这时将执行完整同步操作。

2.2 PSYNC命令的实现

在了解了部分重复制的原理之后,就可以来了解PSYNC命令的完整细节了。其调用方式有两种:

根据情况,主服务器会返回如下三种情况的回复:

PSYNC同步流程

复制的详细步骤

向从服务器发送SLAVEOF <master ip> <master port>,就可以让从服务器去复制主服务器。

下面将复制的步骤详细展开

1. 设置主服务器的地址和端口

当客户端执行slaveof命令时,服务器首先要将主服务器的IP地址和端口号保存在masterhost属性和masterport属性里面:

struct redisServer {
    // 主服务器地址
    char  *masterhost;
    // 主服务器端口
    int masterport;
};

slaveof 命令时一个异步命令,完成属性设置之后,从服务器将向发送slaveof命令的客户端返回OK,表示复制指令已经被接收,然而真正的复制工作在OK返回之后才开始执行。

2. 建立套接字连接

从服务器获取了主服务器的IP和端口后,创建和主服务器的套接字连接。

1)连接成功后,从服务器将为这个套接字关联一个专门用于处理复制工作的文件事件处理器,负责执行后续的复制工作,如接收RDB文件、接收传播命令等。

2)主服务器则将从服务器当做一个客户端来对待,为该套接字创建相应的客户端状态,即从服务器是主服务器的客户端

3. 发送PING命令

从服务器成为了主服务器的客户端之后,首先发送一个PING命令,它起到两个作用:
1)检查通信状态是否正常
2)检查主服务器是否可以正常处理请求,因为复制工作接下来的步骤都必须在主服务器可以正常处理命令请求的状态下才能进行

收到的回复一般有三种情况:

4. 身份验证

从服务器收到“PONG”回复后,下一步就是要进行身份验证,如果从服务器开启了masterauth选项的话,则进行身份验证。

从服务器身份验证情况分析:

身份验证流程

5. 发送端口信息

从服务器将通过命令REPLICONF listening-port,将自身的监听端口发送给主服务器,主服务器将会记录在redisClient对象的slave_listening_port属性中:

typedef struct redisClient {
    int slave_listening_port;
}

目前该属性的唯一作用就是在执行INFO REPLICATION时候,显示出各个从服务器的端口号。

6. 同步

从服务器将主服务器发送PSYNC命令,执行同步操作。同步操作执行完成之后,主从服务器都是对方的客户端,它们可以相互向对方发送命令请求,或者互相向对方返回命令回复。

主从互为客户端

7. 命令传播

完成同步之后,主从服务器就进入命令传播阶段,这时主服务器只要将自己的写命令发送给从服务器即可。

心跳检测

命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令: REPLCONF ACK <replication_offset> (replication_offset就是从服务器当前的复制偏移量)。

这个命令有三个作用:
1)监测主从之间的网络连接状态
2)辅助实现min-slaves选项
3)监测命令丢失

1. 监测主从服务器的网络连接状态

通过主服务器的INFO replication命令,在列出的从服务器列表的lag一栏,我们可以看到相应的从服务器最后一次发送心跳的时间,一般情况下,lag的值应该在0到1秒之间跳动,如果超出了1秒,那么说明主从服务器之间出现了故障。

image.png

2. 辅助实现min-slaves选项

Redis的 min-slaves-to-write 和 min-slaves-max-lag 两个选项配置可以防止服务器在不安全的情况下执行写命令。

如:
min-slaves-to-write 3
min-slaves-max-log 10
那么在从服务器的数量少于3个,或者三个从服务器的延时(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令。

3. 检测命令丢失

如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么从服务器发送的心跳请求,其中包含的偏移量必定小于主服务器的,主服务器会在复制积压缓冲区里面找到丢失的命令,发送给从服务器。

回顾

本篇主要介绍了Redis的复制机制,包括新旧复制功能的实现、如何实现增量同步(命令传播)、新复制功能是如何解决断线重复制问题(offset)。

上一篇 下一篇

猜你喜欢

热点阅读