Redis使用及源码剖析-19.Redis复制-2021-2-4

2022-02-22 13:46:34 浏览数 (1)

文章目录

  • 前言
  • 一、同步实现
    • 1.完整重同步
    • 2.部分重同步
      • a.复制偏移量
      • b.复制积压缓冲区
      • c.服务器运行id
  • 二、复制的完整过程
    • 1.设置主服务器的地址和断开
    • 2.建立套接字连接
    • 3.发送ping命令
    • 4.身份认证
    • 5.发送端口信息
    • 6.同步
    • 7.命令传播
  • 总结

前言

Redis中用户可以执行slaveof命令让一个服务器去复制另一个服务器,被复制的服务器称为主服务器,另一个服务器称为从服务器。例如服务器127.0.0.1:6379从服务器127.0.0.1:12345复制内容的命令如下所示:

代码语言:javascript复制
127.0.0.1:6379>slaveof 127.0.0.1:12345

redis复制分为两步实现:同步和命令传播。从服务器复制主服务器器时,首先要将从服务器的数据库状态更新到和主服务器相同,这一过程称为同步。两个服务器数据状态相同后,主服务器每执行一个写命令,也会将其发送给从服务器,这个过程称为命令传播。

一、同步实现

要进行同步时从服务器会向主服务器发送psync命令,同步分为完整重同步和部分重同步。

1.完整重同步

在从服务器首次复制主服务器或者从服务器和主服务器数据相差过多时,redis采用完整重同步。完整重同步实现如下: a.主服务器收到从服务器的psync命令以后,执行BGSAVE命令在后台生成RDB文件,并用一个缓冲区记录从现在开始的写命令。 b.主服务器将RDB文件发送给从服务器,从服务器收到RDB文件后,根据RDB文件将数据库状态更新到主服务器执行BGSAVE的状态。 c.主服务器将缓冲区的写命令发送给从服务器,从服务器依次执行写命令,最终主从服务器数据状态一致。

2.部分重同步

当从服务器和主服务器完成同步以后,若因为网络故障断开连接一段时间,此时主服务器又执行了一些写操作导致两者数据库状态不一致。此时,若直接进行完整重同步则耗时过长,应考虑进行部分重同步,只将断开连接这一段时间内执行的写命令发送给从服务器进行同步。那么部分重同步如何实现呢?

a.复制偏移量

进行复制的主从服务器都会有一个复制偏移量,当两者数据相同时,复制偏移量都是相同的,比如offset都为10086。此时若主服务器向从服务器发送N个字节的数据主服务器的复制偏移量就变为10086 N,从服务器收到主服务器N个字节数据时,也将自己的offset改为10086 N。以下图为例,最开始主服务器和三个从服务器复制偏移量均为10086,后来从服务器A掉线,主服务器和从服务器BC传播数据以后复制偏移量变为10119.当从服务器A上线后,就可以发现自己的复制偏移量和主服务器的不一致,并知道相差多少数据了。

b.复制积压缓冲区

主服务器给从服务器保留了默认大小为1MB的复制积压缓冲区,这个缓冲区是先进先出的队列。当主服务器执行写命令时,除了将命令发送给从服务器以外,还会将命令放入复制积压缓冲区内。复制积压缓冲区中会为命令的每个字节记录对应的偏移量,示意图如下:

这样当从服务器断线重连后,如果发现自己的复制偏移量和主服务器不一致,这时就会通过psync命令将自己的偏移量发送给主服务器。若从服务器复制偏移量对应的数据还在复制积压缓冲区,则主服务器执行部分重同步,将复制积压缓冲区中该偏移量之后的数据发送给从服务器。否则,将执行完整重同步。

c.服务器运行id

每个主服务器和从服务器都有一个独一无二的运行id,从服务器首次复制主服务器时,主服务器会将id发送给从服务器,从服务器将其保存。这样从服务器断线重连以后,会将保存的主服务器id发送给主服务器。若主服务器发现id和自己id一致,则会根据复制偏移量和复制积压缓冲区数据来判断执行部分重同步或者完整重同步,否则说明从服务器之前复制的不是本服务器,将会直接进行完整重同步。

二、复制的完整过程

1.设置主服务器的地址和断开

客户向从服务器发送以下命令以后:

代码语言:javascript复制
127.0.0.1:6379>slaveof 127.0.0.1:12345

从服务器首先会将主服务器的ip和端口记录在自己的redisserver结构体中,如下所示:

代码语言:javascript复制
struct redisServer {
    // 主服务器的地址
    char *masterhost;               /* Hostname of master */
    // 主服务器的端口
    int masterport;                 /* Port of master */
};

保存完毕后slaveof命令就会返回ok,实际复制工作将在后续进行,这是一个异步命令。

2.建立套接字连接

保存ip和端口后,从服务器会根据套接字地址和主服务器建立套接字连接,并通过该连接向主服务器发送命令以及接收数据。主服务器accept到该连接后,会将从服务器当做一个客户端进行处理。

3.发送ping命令

建立连接以后,从服务器会想主服务器发送ping命令,用于测试两者之间的网络质量以及主服务器能否正常处理命令。

4.身份认证

ping命令返回pong后,若从服务器设置了masterauth选项,将会进行身份验证。

代码语言:javascript复制
struct redisServer {
    /* Replication (slave) */
    // 主服务器的验证密码
    char *masterauth;               /* AUTH with this password with master */
};

5.发送端口信息

身份认证通过以后,从服务器会将自己的监听端口号发送给主服务器,主服务器会将它保留在从服务器对应的客户端对象的slave_listening_port成员中。

代码语言:javascript复制
struct redisClient{
	// 从服务器的监听端口号
    int slave_listening_port; /* As configured with: SLAVECONF listening-port */
};

6.同步

从服务器向主服务器发送psync命令,若是首次复制则命令格式为psync ?-1;若为断线重连,则命令为psync runid offset。在执行同步操作时,主服务器也会成为从服务器的客户端,这样主服务器就可以向从服务器发送数据了。同步操作执行完成后,若是初次复制,主服务器还会将自己的runid和复制偏移量offset发送给从服务器。

7.命令传播

同步完成以后,就进入命令传播阶段,主服务器会将写命令传播给从服务器。同时,从服务器也会以每秒一次的频率向主服务器发送心跳命令,进行心跳检测,防止连接断开或者命令丢失。

总结

本文对redis的复制内容进行了简要介绍,如有不当,请指正。

0 人点赞