redis之主从复制
主从模式
主从结构是常用的计算机系统架构,通常被用于分布式系统中,其中一个节点master
拥有最新的数据,其他节点slave
复制并同步主节点的数据。
主从结构中,主节点负责写入数据,并将这些数据同步到从节点中;从节点只能读取数据。主从节点键通过网络连接,完成数据同步。
主从模式也常被用于数据库系统中,提供高可用能力。当主节点发生故障或者失效时,从节点可以被选举为新的主节点,保证系统的可用性。
主从模式的优点
- 负载均衡:读写分离:提高服务器的性能。
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的数据冗余手段。
- 高可用基石:主从模式是哨兵模式和集群模式的基础。
主从模式的缺点?
- 主从模式不具备自动容错和恢复功能,主节点故障,集群无法工作,可用性较低。从节点升为主节点需要人工手动干预。
为什么redis要使用主从模式?
在了解redis
主从结构的原理前,先来了解一下分布式系统的理论基石 CAP
原理:
- C, Consistent, 即一致性;
- A, Availability, 即可用性;
- P, Partition, 即分区容忍性。
redis复制过程主从节点之间网络故障时,不能满足强一致性,故而只能满足AP。
如何使用主从复制?
redis 2.8
对主从复制功能进行了优化,早先的版本主从复制功能的实现包含两个步骤:
- 同步: 用于将从服务器的服务器状态更新至主服务器当前的数据库状态。
- 命令传播:用于将 主从同步过程中发生的主服务器的修改命令同步至从服务器,以防止主从数据库状态不一致的情况。
slave of ip port
该命令的作用?
复制过程
主从服务器建立链接
从服务器作为客户端向主服务器发送slaveof master_ip master_port
旧版本复制功能的实现?
旧版本主从复制包含同步
和命令传播
两个部分,如下是整体流程:
旧版复制的缺陷?
主从复制过程有如下两个场景:
- 初次复制: slave未同步过任何master,或者当前待复制的主服务器和上一次复制的主服务器不同
- 断线后复制:处于命令传播阶段主从服务器因为网络故障而中断,而后网络恢复后继续同步数据库状态。
在旧版复制功能中,主从服务器发生网络故障后,主从服务器之间数据库状态不一致,slave会向主服务器发送sync
命令,主服务器再次生成包含当前数据库状态的rdb
文件,而后发送给从服务器,从服务器载入rbd
文件后继续执行主服务器发来的写命令。
上述过程的缺陷是,已经同步过的主从节点断联之后,再次连接时需要主服务器生成包含其当前数据库状态的rdb
文件,更为关键的是,此网络故障期间,主节点可能只执行了少数写命令,而需要付出全量更新的开销。
sync命令的开销
sync
命令,主从服务器需要执行以下动作:
- 主服务器需要执行
bgsave
命令生成rdb
文件,此操作会耗费主服务器的大量cpu、内存和磁盘io资源。 - 主服务器将生成的
RDB
文件发送给从服务器,此操作耗费主从服务器的大量网络资源,并对主服务器的命令响应事件产生影响。 - 从服务器载入收到的
rdb
文件前,无法处理读请求。
全量复制过程?
redis 2.8之前?
sync
旧版本复制的缺陷?
为了解决旧版本复制功能在处理断线重复复制时的低效问题,redis 2.8
之后使用psync
命令代替sync
来执行复制时的同步操作。psync
命令提供两种模式:
- 完整重同步: 主服务器创建并发送rdb文件,向从服务器发送保存在缓冲区中的写命令进行同步。
- 部分重同步:用于处理断线后的复制情况。断线重连后,如果情况允许,主服务器将主从断联期间执行的写命令发送给从服务器,从服务器接收并执行写命令,完成数据库状态同步。
那么,psync
的改进即是感知主从是断联场景,这是如何实现的呢?
部分重同步的实现?
部分重同步功能的实现依赖三个结构:
- 主从服务器的复制偏移量 和从服务器的 复制偏移量。
- 主服务器的复制积压缓冲区。
- 服务器的运行id。
相应的定义在server.h
,注意到,在server
中,与主从复制相关的结构定义包含两部分,分别是主服务器配置和从服务器配置。
1 | /* Replication (master) */ |
从服务器配置:
1 | /* Replication (slave) */ |
复制偏移量
执行复制的双方分别维护一个复制偏移量。[主服务器应当维护多个?]
- 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量+N
- 从服务器每次收到主服务器的N个字节的数据时,将自身的复制偏移量+N
通过对比主从服务器的复制偏移量,可以得出主从节点是否处于一致状态。
如果偏移量一致,则主从服务器处于抑制状态;否则处于非一致状态。
注意,主服务器配置中复制偏移量为master_repl_offset
,而从服务器中,
关于下笔时的问题,主服务器中是否应当维护多个复制偏移量,答案是否定的,在源码定义中未找到对应的定义。可以理解为,一主多从时,服务器会将当前数据库状态传递给多个从服务器,并同步复制偏移量。
而redis 4.0
之后,所有的从服务器都会从主服务器接收完全相同的复制量,但是由每队主从之间的网络不一定相同,所以需要从服务器自身维护其当前的复制偏移量。那么问题来了,如何保障下一次同步呢?
这里从节点之间的不一致会影响集群同步状态,主节点向从节点发送主从差别状态时就会变得繁琐。
复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度的FIFO
队列,默认大小为1MB
。
当主服务器进行命令传播时,不仅仅会将写命令发送给从服务器,还会讲命令写入到复制积压缓冲区中。复制积压缓冲区会记录每个字节记记录相对应的复制偏移量。
如果主从断联一段时间后,从服务器重新连上主服务器,则从服务器会通过PSYNC
命令将自己的复制偏移量发送给主服务器,主服务器根据从服务器的复制偏移量决定接下来该如何同步。
- 如果offset偏移量之后的数据依然存在于复制积压缓冲区中,那么主服务器将对从服务器执行部分重同步操作,即主服务器向从节点发送断联期间执行的命令。
CONTINUE
- 如果offset之后逇数据不存在复制积压缓冲区中,则主服务器对当前的从服务器执行完整的重同步操作。
复制积压缓冲区大小?
默认为1MB
,如果主服务器需要执行大量写命令或者主从断联时间较长,则可能需要考虑设置合理的复制积压缓冲区大小。
有这样的计算公式: second * write_size_per_second
即对每秒写入数据量和主从断联恢复时长有一定的预估。
为了安全期间,还需要将复制积压缓冲区的大小进行double
处理。
复制积压缓冲区实现
复制积压缓冲区由一个环形队列实现。的相关定义包含两部分:
1 | replBacklog *repl_backlog; |
repl_backlog_size
: 指定复制积压缓冲区的大小;replBacklog *repl_backlog
: 所有从服务器共享的复制积压缓冲区。
服务器运行ID
实现部分重同步的过程需要用到服务器运行ID
- 每个服务器(主从)都有自己的运行ID
- 运行ID在服务器启动时生成,由40位随机十六进制的字符串组成。
主从服务器初次进行同步时,主服务器将自身的运行ID传递给从服务器,从服务器将其保存至master_replid
中,同时保存初次同步的offset
。
1 | 主服务器配置 |
1 | 从服务器配置 |
主从重连时,从服务器向当前连接的主服务器发送之前保存的运行ID,如果匹配且offset任保存在主服务器的复制积压缓冲区中,则执行部分重同步。若不再则执行完整重同步;否则执行完整重同步操作。
PSYNC实现
PSYNC的调用方式
- 从服务器未复制过任何主服务器,或者执行过
slave no one
,则从服务器再执行第一次复制时将向主服务器发送PSYNC ? -1
,主动向主服务器请求完整重同步。 - 已复制过,则开始新的复制时向主服务器发送
PSYNC <runid> <offset>
, 主服务器判断进行何种操作。
主服务器有三种响应:
- 返回
+FULLSYNC <runid> <offset>
回复,则表示主从服务器之间将进行完整重同步过程; - 返回
+CONTINUE
,则主从服务器之间将执行部分重同步操作,从服务器将等待主服务器发送复制积压缓冲区中堆积的写命令,接收后执行即可。 - 返回
-ERR
,则表示主服务器版本低于redis 2.8
无法识别PSYNC
命令。从服务器将向主服务器发送SYNC
命令,执行完整同步过程。
完整流程
心跳检测
心跳检测是指,在命令传播阶段,从服务器会以默认每秒一次的频率向服务器发送命令:replconf ack <replication_offset>
, replication_offset
是从服务器当前的复制偏移量。
上述命令包含三个作用:
- 检测主从服务器之间的网络连接状态。
- 辅助实现
min-slaves
选项。 - 检测命令丢失。
检测主从网络连接状态
如果主服务器超过一秒钟未收到从服务器发来的replconf ack
命令,那么服务器则知道两者的网络连接出问题了。
在主服务器上执行info replication
命令,tag栏能反映出当前从服务器最后一次向主服务器发送replconf ack
距此时过了多久:
1 | 182.168.106.129:6379> info replication |
注意上述返回中从节点数为0是因为本人所使用的redis的模式为集群模式。在后续内容中将补充redis的架构模式。
辅助实现min-slaves
选项
redis中如下两个配置可以防止主服务器在不安全的情况下执行写命令。
1 | min-slaves-to-write 3 |
分别对应server
中的配置:
1 | int repl_min_slaves_to_write; /* Min number of slaves to write. */ |
其含义为:在从服务器少于3个或者三个从服务器的延迟大于等于10秒时,主服务器将拒绝少执行命令。
检测命令丢失
如果因为网络故障,主服务器传播给从服务器的写命令半路丢失,那么当从服务器向主服务器发送replconf ack
时,主服务器将识别到主从之间的复制偏移量存在差异,而后主服务器就根据从服务器提交的偏移量,在复制积压缓冲区中找到从服务器缺少的数据,讲这些数据重新发给从服务器。
乍看起来,这个过程和部分重同步非常相似,但是两者还是有些差异:
补发缺失数据操作在主从服务器之间没有断线时执行;而部分重同步发生在断线重连之后。
注意到 redis 2.8
之前没有replconf ack
和 复制积压缓冲区
,即使命令在传播过程中丢失,主从都不会意识到,主服务器也不会向从服务器补发丢失的数据。
思考
无盘复制是什么?
redis
默认是磁盘复制,但是如果使用低速磁盘,复制操作会给主服务器带来较大压力。所以redis 2.8.18
后开始支持无盘复制
。在这种模式下,子进程直接将rdb
文件通过网络发送给从服务器,不适用磁盘作为中间存储。
redis主从复制过程为什么选择rdb,而非aof
rdb
文件内容是经过压缩的二进制数据,同时redis
针对不同的数据类型做了针对性优化,文件较小。而aof
文件记录的是每一次写操作的命令,写操作越多文件越大,而且包含对重复key的冗余操作。在主从全量同步时,传输rdb
文件可以降低对主从服务器的网络带宽开销。从库在加载RDB
文件时,文件小,读取快。同时从库按照rdb协议解析还原数据即可。而aof
需要依次重放每个写命令,恢复速度比rdb
慢很多。- 假设使用
AOF
做全量复制,则服务器必须打开aof
功能,必须选择文件刷盘的策略,选择不当会严重影响redis
性能。而RDB
只有在需要定时备份和主从全量复制时才会触发,生成快照。在很多丢失数据不敏感的业务场景,其实是不需要开启AOF
的。
如何理解主-从-从模式
如果是主从模式,一主多从的情况下,如果多个从服务器向主服务器请求全量复制,在主库中需要完成多次fork
子进程生成RDB
文件,进行全量复制,fork操作会阻塞主线程处理正常请求。另外传输RDB
文件也会占用主库网络带宽。
可以通过主-从-从模式建立多级主从模式,以缓解顶级主服务器的压力。
如何理解redis的高可用
- 数据不能丢失或尽量减少丢失。
- redis服务不中断。
相对应的,第一点由持久化机制aof和rdb
保障;第二点则要求redis
不能单点部署。
主从不一致的原因?
- 主从网络时延大/断联
- 从库收到主库发来的命令,但从库正在执行阻塞式命令,如
hgetall
。
redis 主从模式如何选主?
在主从模式中,主节点故障后,需要人工干预将从节点设置为主节点,同时还需要通知应用方更新主节点地址。故而有了另一种架构模式:哨兵模式
引用
1. redis复制
2. 详解Redis 主从复制原理
3.
4. Redis主从模式的优缺点
5. CAP 定理的含义
6. 极客时间:主从库如何实现数据一致
7. redis主从