复制

  • 完整重同步
  • 部分重同步

部分重同步的实现

  • 复制偏移量
  • 复制积压缓冲区
  • 服务器运行ID

PSYNC过程的实现

  • 建立连接和同步判断(完整/部分)
  • 复制数据

复制(建立连接 + 身份验证 + PSYNC + 数据同步)

建立连接后的心跳检测

  • 每秒发送命令
  • 三个作用(检测网络状态,min-slaves配置,检测命令丢失)

1.复制

  • 在redis中,用户可以通过执行SLAVEOF命令或者配置让一个服务器去复制另一个服务器
  • eg. 向服务器127.0.0.1:12345发送 SLAVEOF 127.0.0.1 6379命令,12345会变成6379的从服务器
  • 从2.8开始,redis使用PSYNC命令来代替SYNC执行复制时的同步操作
  • PSYNC具有两种模式:
  • 完整重同步(full resync)
  • 从服务器发送同步请求(PSYNC)
  • 主服务器执行BGSAVE,在后台生成RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
  • RDB完成后,主服务器发送给从服务器RDB文件
  • 主服务器发送缓冲区中的内容
  • 部分重同步(partial resync)
  • 适用于断线重连情况

2.部分重同步的实现

(1)复制偏移量

复制的双方——主服务器和从服务器分别维护一个复制偏移量

eg.主从服务器现在的复制偏移量都是10086,如果主服务器向从服务器A,B,C传播33字节,而A断线了,则情况如下

synchronized和redisson性能_客户端

(2)复制积压缓冲区

是由主服务器维护的一个固定长度,先进先出的队列,默认1MB。

主服务器在复制时不仅会把命令发送给所有从服务器,也会写入复制挤压缓冲区。如下所示

synchronized和redisson性能_客户端_02

synchronized和redisson性能_客户端_03

当从服务器断线重连时,会通过PSYNC命令将自己的offset发送给主服务器。

主服务器判断offset后面缺失的内容是否在积压缓冲区中:

  • 如果在:则回复+CONTINUE,表示采用部分重同步来恢复。并将缺失的数据发送给从服务器
  • 如果不在:则进行完整重同步

缓冲区大小默认1MB,可以根据conf中repl-backlog-size来修改大小

(3)服务器运行ID

每个服务器,无论主从,都有自己的运行ID:40个随机16进制字符,在服务器启动时自动生成

  • 当从服务器初次复制时,主服务器会发送自己的ID,从服务器会保存。
  • 当从服务器断线重连时,向主服务器发送之前记录的ID
  • 如果相同,说明是断线重连前的那个主服务器,可以尝试进行partial resync
  • 如果不相同,说明主服务器已经换了,必须fully resync

3. PSYNC过程的实现

从服务器发送PSYNC命令,请求复制

  • 如果从服务器以前没有复制过任何主服务器,则向主服务器发送 PSYNC ? -1 的命令,表示请求完整重同步
  • 如果之前已经复制过某主服务器,则向主服务器发送 PSYNC <runid> <offset> ;runid是上次复制的主服务器ID;offset是从服务器当前的复制偏移量

主服务器回复有三种情况:

  • +FULLRESYNC <runid> <offset> : 执行 fully sync,offset是主服务器的复制偏移量
  • +CONTINUE:执行 partial sync,从服务器等待主服务器发送缺失的数据
  • -ERR:主服务器版本低于2.8,识别不了PSYNC命令,需要从服务器发送SYNC命令

例子:

(1)从服务器的客户端——>从服务器: slaveof 127.0.0.1 6379

(2)从服务器——>主服务器:PSYNC ? -1

(3)主服务器——>从服务器:FULLRESYNC 53b9b28df8042fdc5ab5e3fcbbbabff1d5dce2b3 10086

(4)完整重同步成功,主从服务器保持一致,offset一直增加

(4)offset同步到20000字节时,从服务器断开重连

(5)从服务器——>主服务器:PSYNC 53b9b28df8042fdc5ab5e3fcbbbabff1d5dce2b3 20000

(6)主服务器——>从服务器: + CONTINUE (发现缺失的数据仍在缓冲区中,可继续复制)

4.复制的实现

(1)从服务器设置地址和端口

当从客户端发送salve指令时,从服务器会存储对于IP地址和端口号到redisServer中去。

当设置属性完成后,从服务器返回给客户端OK指令。而实际的复制工作将在之后才执行

struct redisServer{
    
    //主服务器地址
    char *masterhost;

    //主服务器端口
    int masterport;

}

(2)从服务器建立socket连接

从服务器根据IP和端口号,向主服务器建立连接。

连接建立后,从服务器将为这个socket关联一个handler

这时,从服务器可以被当作 主服务器的客户端。

(3)从服务器发送发送PING命令

从服务器成了主服务器的客户端之后,第一件事是发送PING命令,主服务器会有三种回复

  • 返回了一个回复超时——>网络状态不好,从服务器断开并重新连接
  • 返回一个ERROR——>主服务器可能在忙,从服务器断开并重新连接
  • 返回PONG——>成功,可以继续下一步

(4)身份验证

从服务器收到PONG之后,进行身份验证,主要根据conf中的配置。

如果设置了masterauth,则从服务器会自动发送 AUTH password指令给主服务器进行验证

  • eg.如果主服务器设置了密码为10086, 则从服务器会发送 AUTH 10086
//conf配置文件
slaveof <masterip> <masterport>    //设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步

masterauth <master-password>    //作为slave时,需要给master提供的密码

requirepass foobared    //本服务器自身的密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭

连接成功的情况:

  • 主服务器没有设置requirepass,从服务器也没有提供masterauth
  • 主服务器设置了requirepass,从服务器也提供了masterauth。两者相同

其他情况都连接不成功

(5)发送端口信息

身份验证成功后,从服务器发送 REPLCONF listening-port <port-number>命令,向主服务器发送从服务器的监听端口号,主服务器记录在对应redisClient中

synchronized和redisson性能_偏移量_04

typedef struct redisClient{

    //从服务器的监听端口号
    int salve_listening_port
}

该属性的唯一作用是:主服务器查看所有slave时:执行 INFO replication命令,打印所有从服务器的端口号

(6)同步

这一步中,主服务器和从服务器都是各自的客户端。执行PSYNC命令

(7)命令传播

完成同步之后,主服务器只要将自己执行的写命令同步给从服务器,就可以一直保持一致。

5.心跳检测

  • 从服务器会以每秒一次的频率,向主服务器发送命令,其中replication_offset是从服务器当前的offset
  • REPLCONF ACK <replication_offset>
  • 主要有三个作用
  • 检测主服务器的网络状态
  • 如果想看某一主服务器的所有从服务器延迟信息,可以发送 INFO replication指令
  • 根据时间可以看到lag,正常的lag应该在0到1秒之间。如果超过了一秒,说明连接出了故障
  • 辅助实现min-slaves配置选项,有两个配置与此相关
  • min-slaves-to-write 3       如果从服务器的数量<3个,主服务器拒绝执行写命令
  • min-slaves-max-lag 10        如果3个从服务器的lag都>=10秒时,主服务器拒绝执行写命令
  • 检测命令丢失
  • 主服务器会根据发送过来的offset检测,是否与从服务器保持数据一致,如果不一致,则部分重传。