目录
旧版复制功能的实现
同步
命令传播
缺陷
新版复制功能
部分重同步的实现
复制偏移量
复制积压缓冲区
服务器运行ID
PSYNC命令
复制的实现
心跳检测
检测主从服务器网络连接状态
辅助实现min-slaves选项
检测命令丢失
在Redis中,用户可以通过执行SLAVEOF 命令或者设置slaveof选项,让一个服务器去复制(replicate)另一个服务器,被复制的服务器为主服务器(master),进行复制的服务器为从服务器(slave)。
SlaveOf 127.0.0.1 6379
旧版复制功能的实现
Redis 的复制功能分为同步(sync)和命令传播(command propagate)两个操作:
- 其中, 同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。--即初始化从服务器。
- 而命令传播操作则用于在主服务器的数据库状态被修改, 导致主从服务器的数据库状态出现不一致时, 让主从服务器的数据库重新回到一致状态。--即同步变更。
同步
当客户端向从服务器发送 SLAVEOF 命令, 要求从服务器复制主服务器时, 从服务器首先需要执行同步操作, 也即是, 将从服务器的数据库状态更新至主服务器当前所处的数据库状态。
从服务器对主服务器的同步操作需要通过向主服务器发送 SYNC 命令来完成, 以下是 SYNC 命令的执行步骤:
- 从服务器向主服务器发送 SYNC 命令。
- 收到 SYNC 命令的主服务器执行 BGSAVE 命令, 在后台生成一个 RDB 文件, 并使用一个缓冲区记录(记录变更增量)从现在开始执行的所有写命令。
- 当主服务器的 BGSAVE 命令执行完毕时, 主服务器会将 BGSAVE 命令生成的 RDB 文件发送给从服务器, 从服务器接收并载入这个 RDB 文件, 将自己的数据库状态更新至主服务器执行 BGSAVE 命令时的数据库状态。
- 主服务器将记录在缓冲区里面的所有写命令发送给从服务器, 从服务器执行这些写命令, 将自己的数据库状态更新至主服务器数据库当前所处的状态。
注意:
1、从服务器在同步时,会清空所有数据,服务器在与主服务器进行初连接时,数据库中的所有数据都将丢失,替换成主服务器发送的数据。
2、Redis不支持主主复制
3、主从复制不会阻塞master(不会阻塞master处理客户端请求),相反slave在初次同步数据时会阻塞不能处理客户端请求。
4、当多个从服务器尝试连接同一个主服务器的时候,就会出现以下两种情况:
- 一是:步骤3还未执行,所有从服务器都会接收到相同的快照文件和相同缓冲区写命令。
- 二是:步骤3正在执行或者已经执行完毕,当主服务器与较早的从服务器完成以上全部步骤之后,主服务器会新连接的从服务器重新依次执行1-5步骤。
5、在大部分情况下,Redis会尽可能去减少复制所需要的工作,但是从服务器连接的时机不凑巧的话,只好多做一些外额外工作。
多个从服务器连接主服务器时候,同步数据可能会占用很大一部分的带宽,可能会导致其他请求难以到达主服务器。
命令传播
在同步操作执行完毕之后, 主从服务器两者的数据库将达到一致状态, 但这种一致并不是一成不变的 —— 每当主服务器执行客户端发送的写命令时, 主服务器的数据库就有可能会被修改, 并导致主从服务器状态不再一致。
为了让从服务器再次回到一致状态, 主服务器需要对从服务器执行命令传播操作: 主服务器会将自己执行的写命令 —— 也即是造成主从服务器不一致的那条写命令 —— 发送给从服务器执行, 当从服务器执行了相同的写命令之后, 主从服务器将再次回到一致状态。
缺陷
主要是主从服务器断线后重复制,即处于命令传播阶段的主从服务器由于网络断开,从服务器一直尝试连接主服务器连接成功后,继续复制主服务器。
从服务器连接后发送SYNC命令,重新进行了“全量”复制过程,RDB文件中包含全部的键。此操作效率特别低。
SYNC命令非常消耗资源,原因有三点:
- 主服务器执行BGSAVE命令生成RDB文件,这个生成过程会大量消耗主服务器资源(CPU、内存和磁盘I/O资源)
- 主服务器需要将自己生成的RBD文件发送给从从服务器,这个发送操作会消耗主从服务器大量的网络资源(带宽与流量)
- 接收到RDB文件你的从服务器需要载入RDB文件,载入期间从服务器会因为阻塞而导致没办法处理命令请求。
新版复制功能
为了解决旧版本中断线情况下SYNC低效问题,在Redis 2.8之后使用PSYNC命令代替SYNC命令执行复制同步操作,自然PSYNC具备完整重同步(full resynchronization)和部分重同步模式(partial resynchronization)
- 完整重同步:跟旧版复制基本是一致的,可以理解为“全量”复制。
- 部分重同步:在命令传播阶段,断线重复制只需要发送主服务器在断开期间执行的写命令给从服务器即可,可以理解为“增量”复制。
部分重同步的实现
部分重同步功能由以下3个部分组成:
- 主服务的复制偏移量(replication offset) 和 从服务器的复制偏移量
- 主服务器的复制积压缓冲区(replication backlog)
- 服务器的运行ID
复制偏移量
执行复制的主服务器、从服务器会分别维护一个复制偏移量:
- 这个偏移量记录了执行命令的字节数。主服务器每次向从服务器传播N个字节时就会将自己的复制偏移量+N
- 从服务在接收到主服务器传送来的N个字节的命令时,就将自己的复制偏移量+N。
通过对比复制偏移量,可以知道主从服务器是否一致:
- 2者相同,主从一致。
- 不相同,主从不一致。
复制积压缓冲区
复制积压缓冲区是一个由主服务器维护的固定长度的先进先出队列,默认大小为1M,可调整(repl-backlog-size)。
当从服务器重新连上主服务器时,通过PSYNC发送复制偏移量(offset),主服务器根据其值决定如果操作:
- offset之后的数据仍在复制积压缓冲区,则主服务器对从服务器执行部分重同步操作。
- 不在,执行完整重同步。
Redis复制积压缓冲区的大小可以设置,一般将其设置为 second * write_size_per_second * 2的大小
服务器运行ID
每个Redis 服务器无论主服务器还是从服务器都有自己的运行ID
运行ID在服务器启动时生成,由40个随机的十六进制字符组成
当从服务器对主服务器进行初次复制时,主服务器会将自身的运行ID回传给从服务器,从服务器会将这个运行ID保存下来。
当从服务器断线重连主服务器时,会将这个运行ID发送个主服务器,主服务器会检查这个运行ID是否为自己的运行ID,如果不是则会直接执行完整重同步,如果是自己的运行ID,则根据具体情况来决定是采用部分重同步还是完整重同步。
PSYNC命令
PSYNC命令调用方法有2种:
- 请求完整重同步: PSYNC ? -1
- 请求重连接同步:PSYNC <master runid> <offset>
主服务器返回结果:
- 执行完整重同步:+FULLRESYNC <master runid> <offset>
- 部分从同步:+CONTINUE
- 不识别命令:-ERR
复制的实现
1.设置主服务器的地址和端口
SLAVEOF 127.0.0.7 6379
2.建立套接字连接
3.发送PING命令,检查是否可以连通主服务器。(返回PONG表示连通)
4.身份验证
从服务器向主服务器发送一条AUTH
命令,将密码带给主服务器。
masterauth 用于设置从服务器是否进行身份验证,AUTH命令的参数为此值。
requirepass 用于设置主服务器是否需要密码验证。
结果:
- 主服务没有requirepass,从服务器没有masterauth。复制继续进行。
- requirepass与masterauth相同。复制继续进行。不同返回:invalid password。
- 主有requirepass,从没有masterauth。返回:NOAUTH
- 主没有requirepass,从有masterauth。返回:no password is set
5.发送端口信息
执行命令
REPLCONF listening-port <port-number>
向主服务器发送从服务器监听端口
6.同步
7.命令传播
心跳检测
在命令传播阶段,从服务器默认会以每秒一次的频率发送命令:
REPLCONF ACK <replication_offset>
其中replication_offset是从服务器当前的偏移量
这个命令主要有三个作用:
- 检测主从服务器的连接状态
- 辅助实现min-slaves选项
- 检测命令丢失
检测主从服务器网络连接状态
执行如下命令:
127.0.0.1:9001> INFO replication
# Replication
role:master
connected_slaves:1
# lag值表示1s前发送过REPLCONF ACK
slave0:ip=127.0.0.1,port=9004,state=online,offset=293566,lag=1
master_replid:1bb39e63541f367d670879f7be07f49855d96aad
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:293580
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:293580
一般情况下lag值应该在0秒或1秒之间跳动,如果超过了1秒那么说明主从服务器之间连接出了问题
辅助实现min-slaves选项
Redis的min-slaves-to-write 和 min-slaves-max-lag 这两个选项可以防止主服务器在不安全的情况下执行写命令。
例如我们进行如下配置:
min-slaves-to-write 3
min-slaves-max-lag 10
那么在从服务器数量少于3个
或者三个从服务器的延迟(lag)值都大于或等于10时,主服务器将拒绝执行写命令。
检测命令丢失
如果因为网络故障,主服务器传播给从服务器的写命令丢失,那么当从服务器向
主服务器发送REPLCONF ACK <replication_offset>
命令时,主服务器会发现从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务就会根据从服务器提交的复制偏移量在复制积压缓冲区中找到从服务器中缺少的数据,并将这些数据重新发送给从服务器。