搭建主从复制

首先修改主节点配置文件

daemonize yes //开启守护线程

logfile "6379.log"

dbfilename "dump-6379.rdb"

protected-mode no //关闭代表外部网络可以直接访问,开启需配置bind ip或者访问密码

port 6379

bind 0.0.0.0

复制代码

然后复制两份配置文件,将文件名进行更改

docker redis 主从复制value error_redis

然后对从节点配置文件进行更改

daemonize yes

logfile "6378.log"

dbfilename "dump-6378.rdb"

protected-mode no

port 6378

bind 0.0.0.0

slaveof 192.168.121.133 6379

复制代码

启动三个节点

./redis-server ../conf/redis-6379-master.conf

./redis-server ../conf/redis-6378.conf

./redis-server ../conf/redis-6377.conf

复制代码

进入客户端

./redis-cli -p 6379

复制代码

docker redis 主从复制value error_redis_02

可以看到有两个从节点

再来看看从节点情况

docker redis 主从复制value error_redis_03

可以看到是从节点

主从搭建就成功了

主从复制原理

主从复制在Redis2.8之前和redis2.8之后有索改动,这里会分别讲解这两者的实现原理

旧版复制

redis复制功能分为同步(sync)和命令传播两个操作

  • 同步操作用于将从服务器数据库状态更新至和主服务器当前所处状态 保持相同
  • 命令传播用于主服务器的数据库状态被修改时,导致主从数据库状态不一致时,让主从服务器的数据库重新回到一致状态。

同步

当从服务器开始和主服务器建立主从关系时(从服务器发送slaveof命令),从服务器首先需要执行同步操作,从服务器对主服务器的同步操作是需要通过向主服务器发送SYNC命令来完成

  • 从服务器向主服务器发送SYNC命令
  • 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓存区记录从现在开始执行的所有写命令。
  • 主服务器BGSAVE命令完成时,主服务器会将生成的RDB文件发送给从服务器,从服务器会将RDB文件的数据读取存储到自己的库中。
  • 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将这些数据进行存储。

docker redis 主从复制value error_redis_04

命令传播

假设一个主服务器和一个从服务器刚完成同步操作,他们数据库状态保持一致,如果这时客户端向主服务器发送写命令,此时主从就不一致了,主服务器会对从服务器执行命令传播操作,主服务器会将自己执行的写命令发送给从服务器执行,从而让主从再次回到一致状态。

旧版复制缺陷

从服务器对主服务器的复制分为两种情况:

  • 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同
  • 断线后复制:处于命令传播阶段的主从服务器因为各种原因中断了复制,但从服务器通过自动重连重新连接上了主服务器,并继续复制主服务器。

对于短线后复制,旧版复制功能效率很低,当重连时,从服务器会向主服务器发送SYNC命令,主服务器会将包含所有数据的RDB文件发送给从服务器,来实现最终的数据一致,这种全量的推送其实是可以优化的,可以只将断线期间没有同步的写命令进行同步,但旧版复制没有实现这个机制。

新版复制

为了解决旧版复制在处理断线复制情况的低效问题,Redis从2.8开始,使用PSYNC命令代替SYNC命令来执行复制时的同步操作。

PSYNC命令具有完整重同步和部分重同步两种模式:

  • 完整重同步用于处理初次复制情况,和旧版SYNC命令步骤基本一致
  • 部分重同步用于处理断线后重复制情况,主服务器可以只将断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可重新保持一致。

部分重复复制实现

部分重复制由一下三个部分构成:

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

执行复制的双方都分别维护一个复制偏移量,主服务器每次向从服务器传播N个字节的数据时,就会将自己的复制偏移量值加上N,从服务器每次收到主服务器传播来的N个字节数据时,就将自己的复制偏移量值加上N。

复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度先进先出的队列,默认大小1MB。

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里。

docker redis 主从复制value error_偏移量_05

当从服务器重新连上主服务器,从服务器会通过PSYNC命令将自己的复制偏移量发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:

  • 如果偏移量之后的数据存在于复制积压缓冲区,那么主服务器将对从服务器执行部分重同步操作。
  • 如果偏移量之后的数据不存在于复制积压缓冲区,主服务器将对从服务器执行完整重同步操作。
服务器运行ID

每个Redis服务器,都会有自己的运行ID.

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器会将运行ID保存起来。

当从服务器短信并重新连上主服务器,从服务器会向当前连接的主服务器发送之前保存的运行ID。

如果从服务器发送的运行ID和当前主服务器运行ID相同,主服务器会先尝试执行部分重同步操作。

如果运行ID不相等,主服务器会对从服务器执行完整重同步操作。

PSYNC 命令实现

  • 如果从服务器以前没有复制过任何主服务器,从服务器开始新的复制将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步。
  • 如果从服务器已经复制过某个主服务器,从服务器开始新的复制会向主服务器发送PSYNC 命令:runid是上次复制的主服务器运行ID,offset是从服务器当前偏移量,主服务器会通过这两个参数来决定执行哪种同步操作。
  • 如果住服务器返回+FULLRESYNC 回复,那么表示是完整重同步操作,如果返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作。如果主服务器返回-ERR,表示主服务器版本低于2.8,它识别不了PSYNC命令,从服务器会发送一个SYNC命令,执行完整重同步。

docker redis 主从复制value error_redis_06

复制整体流程

  • 从节点执行slaveof 命令
  • 从节点保存slaveof命令中主节点信息
  • 从节点内部的定时任务发现有主节点的信息,开始使用socket连接主节点
  • 连接建立成功,发送ping命令,得到ping命令响应,否则会进行重连
  • 主节点设置了权限,就需要进行权限验证
  • 验证通过,进行数据同步

docker redis 主从复制value error_偏移量_07

心跳

命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令

docker redis 主从复制value error_java_08

replication_offset 是从服务器当前的复制偏移量。

发送REPLCONF ACK 命令有三个作用

  • 检测主从服务器网络连接状态

主从服务器会通过这个命令检查两者之间的网络连接是否正常,如果主服务器超过一秒没有收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间连接出了问题

  • 辅助实现min-salves配置选项

Redis可以通过min-slaves-to-write和min-salves-mas-lag两个选项防止主服务器不安全情况下执行写命令

docker redis 主从复制value error_数据库_09

经过设置之后,如果从服务器数量少于3个,或者三个从服务器延迟值都大于或等于10秒时,主服务器拒绝执行写命令。

  • 检测命令丢失

如果因为网络故障,主服务器传播给从服务器写命令半路消失,那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器发送跟从服务器偏移量有差异,就会在复制积压缓冲区找到从服务器缺少的数据,然后重新发给从服务器。