redis中,用户可以通过执行SLAVEOF 命令或设置slaveof选项,让一个服务器复制另一个服务器。
- 主服务器(master):被复制的服务器。
- 从服务器(slave):对主服务器进行复制的服务器。
主从服务器双方数据库状态一致。
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
1. 旧版复制功能的实现
redis的复制功能分为两个操作:
1. 同步(sync)
2. 命令传播(command propagate)
1.1 同步
从服务器向主服务器发送SYNC命令,实现同步操作。
1.2 命令传播
每当主服务器执行客户端发送的写命令时,主服务器的数据库状态可能被修改,导致主从服务器状态不一致。为了让主从服务器再次回到一致状态,主服务器需要对从服务器执行命令传播操作:主服务器将造成主从服务器不一致的命令,发送给从服务器执行,使主从服务器再次回到一致状态。
1.3 旧版复制功能的缺陷
redis中,从服务器对主服务器的复制分两种情况:
1. 初次复制
2. 断线后重复制
旧版复制在处理断线后重复制时,采用和初次复制一样的操作,比较低效。
2. 新版复制功能的实现
新版复制功能使用PSYNC
命令代替SYNC
命令,具有:
1. 完整重同步:处理初次复制情况。
2. 部分重同步:处理断线后重复制情况。
2.1 部分重同步的实现
部分重同步功能由三部分构成:
1. 复制偏移量(replication offset)
2. 复制积压缓冲区(replication backlog)
3. 服务器的运行ID(run ID)
(1)执行复制的双方—主服务器和从服务器,会分别维护一个复制偏移量:主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量+N,从服务器每次收到主服务器传播来的N个字节数据时,复制偏移量+N。
对比主从服务器的复制偏移量,可以判断主从服务器是否处于一致状态。
(2)复制积压缓冲区
主服务器维护的一个固定长度(fixed-size)先进先出队列(FIFO),默认大小为1MB。
当主服务器进行命令传播时,①将写命令发送给所有从服务器;②将写命令入队到复制积压缓冲区。
(3)服务器运行ID
每个redis服务器,主从,都有自己的运行ID,在服务器启动时自动生成。
如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,说明断线前复制的就是当前连接的主服务器,执行部分重同步。
如果从服务器保存的运行ID和当前连接的主服务器的运行ID不同,说明断线前复制的不是当前连接的主服务器,执行完整重同步。
2.1 复制的实现
步骤:
1. 设置主服务器的地址和端口:从服务器在redisServer->masterhost 和 masterport 中保存主服务器的地址和端口。
2. 建立套接字连接
3. 发送PING命令
4. 身份验证
5. 发送端口消息:从服务器向主服务器发送从服务器的监听端口
6. 同步
7. 命令传播
3. 心跳检测
在命令传播阶段,从服务器默认会每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>
,其中replication_offset是从服务器当前的复制偏移量。
作用有三:
1. 检测主从服务器的网络连接状态
2. 辅助实现min-slaves选项
3. 检测命令丢失
(1)主从服务器通过发送和接收REPLCONF ACK
命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没有收到从服务器发来的REPLCONF ACK
命令,则可判断网络连接异常。
(2)redis的min-slaves-to-write
和min-slaves-max-lag
两个选项可以防止 主服务器在不安全的情况下执行写命令。
(3)检测命令丢失
如果因为网络故障,命令传播在半路丢失,那么当从服务器发送
REPLCONF ACK
命令时,主服务器发觉复制偏移量不同,就会从复制积压缓冲区找到从服务器丢失的数据,重新发送。
如果主服务器执行了命令 SET key value (协议格式的长度为 33 字节), 将自己的复制偏移量更新到了 233 , 并尝试向从服务器传播命令 SET key value , 但这条命令却因为网络故障而在传播的途中丢失, 那么主从服务器之间的复制偏移量就会出现不一致: 主服务器的复制偏移量会被更新为 233 , 而从服务器的复制偏移量仍然为 200 , 如图 15-24 所示。
在这之后, 当从服务器向主服务器发送 REPLCONF ACK 命令的时候, 主服务器会察觉从服务器的复制偏移量依然为 200 , 而自己的复制偏移量为 233 , 这说明复制积压缓冲区里面复制偏移量为 201 至 233 的数据(也即是命令 SET key value )在传播过程中丢失了, 于是主服务器会再次向从服务器传播命令 SET key value , 从服务器通过接收并执行这个命令可以将自己更新至主服务器当前所处的状态, 如图 15-25 所示。