目录
一、概念
说起Redis的数据备份,持久化主要用来解决Redis的单机备份,一旦该服务器挂了,数据就会全部丢失。
为了避免这种情况,于是就有了集群概念,将一台Redis服务器的数据复制到其他的Redis服务器上。前者称为主节点(Master),后者称为从节点(Slave)。数据的复制是单向的,只能从主节点到从节点。Master以写为主,Slave以读为主。
这样做的好处有两个:
- 保证数安全,当主节点数据宕机时,自动连接到其他服务器。
- 通过复制功能,可能实现数据库的读写分离,主数据库主要做写操作,从数据库负责读操作。从而提高服务气的负载能力。
二、环境配置
Redis的主从复制只需配置从库,不用配置主库。
在多台服务器上分别安装redis,修改Slave从库配置文件:
-
修改 salveof,添加master主服务器的ip地址 + 端口号
-
若master主服务器有密码,则还需设置 masterauth + 主服务器密码
-
修改相对应的日志文件名称和rdb文件名
查看配置情况:
-
Master主服务器信息
-
Slave从服务器信息
slave从服务器内部维护两个字段,masterhost和masterport,用以保存master主服务器的ip和端口信息。
注:slaveof是异步命令,从服务器保存完主服务器的ip和端口后,向发送slaveof命令的客户端直接返回OK,实际复制操作是在之后才开始进行的。
三、数据操作
Redis主从复制,Master主服务器负责写,Slave从服务器只能get读取,不能写。
主机:
127.0.0.1:6379> key *
(empty list or set)
127.0.0.1:6379> set k1 v1
OK
从机:
127.0.0.1:6379> key *
(empty list or set)
127.0.0.1:6379> key *
1) "k1"
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> set k2 v2
(error) READONLY You can't write against a read only replica.
注:Master主服务器断开连接,Slave从服务器仍然连接到主服务器,但没有写操作。若之后主服务重新连接,从服务器可继续直接获取主服务的写入信息。
四、复制原理
主从复制过程大体可分为3个阶段:连接建立阶段、数据同步阶段和命令传播阶段。
复制的原理主要在数据同步阶段,在Redis2.8之前,同步命令为sync,同步的方式为全量复制;在Redis2.8及之后的版,Slave从节点使用的是psync命令请求同步数据,同步的方式除了原先的全量,也新增了部分复制。
1.全量复制:用于初次复制或其他不满足部分复制的情况时,将主节点的所有数据都发送给从节点。是Redis最早支持的复制方式,也是主从第一次建立复制时必须进行的方式。全量复制是一个非常重型的操作。
2.部分复制:用于网络中断或其他异常时,从节点让主节点补发丢失命令数据的复制,该方法只将复制缓冲区的数据发送给从节点,从而保证数据的一致性,和全量相比更加高效。但若异常时间过长,导致数据不在复制缓冲区中时,仍然只能使用全量复制。
1.全量复制
1)当无法满足从节点发出的部分复制请求时,向主节点器发送全量复制的请求,发送psync ? -1。
2)主节点收到全量复制命令后,返回FULLRESYNC,随之一起发送的还有主节点的runid和偏移量offset。从节点接收到响应后,保存主节点的runid和offset。
3)主节点执行bgsave进行快照操作,后台生成RDB文件。同时还会使用复制缓冲区,记录当前时刻开始的执行的所有写命令。
4)当主节点的bgsave快照操作执行完成后,将RDB文件发送给从节点。从节点清空数据后,加载RDB文件。将数据库状态更新至主节点执行快照的时刻状态。
5)主服务器将记录在缓冲区的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
6)若从节点开启了AOF,则还会触发bgrewriteof,保证AOF文件更新至主节点的最新状态
主从节点打印日志如下:
主节点打印日志:
从节点打印日志:
全量复制是一个非常重型的操作:
- 主节点的bgsave,虽然是fork子进程进行RDB持久化,但当数据量大,非常消耗CPU、内存(页表复制)、硬盘IO的;
- 主节点发送给从节点的RDB文件是通过网络传输的,一旦数据量大,还会造成网络带宽的消耗开销;
- 当从节点接收到主节点发送的RDB文件后,从清空数据到载入新的RDB文件,整个过程时阻塞的,即无法响应客户端命令;
- 从节点开启AOF的话,还会有bgrewrireof开销
所以一般情况下,都尽可能避免用到全量复制,以下几种特例情况必须使用全量复制:
- 第一次建立主从连接数据同步时,肯定走的全量复制;
- 从节点发送psync runid offset时,runid与当前主节点的runid不匹配;
- 从节点所需同步数据的偏移量offset不在复制缓冲区(被挤出),也会进行全量复制;
2.部分复制
由于全量复制的重型操作,在主节点数据量较大时效率低下。因此在Redis2.8之后提供了 部分复制功能,用于处理网络中断的数据同步。
主要有三个概念:
1)复制偏移量 offset
主从节点各自维护一个复制偏移量offset,代表当前节点接收数据的字节数。主节点每次向从节点传播N个字节数据时,主节点offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。
偏移量是用来判断主从节点数据是否一致的唯一标准。主从节点的offset相同,则说明数据一致;不相同则不一致。
在offset偏移量不一致的情况下,比较主从节点的offest值,可以判断二者找出从节点缺失的部分数据。例如,主节点的offset值为200,而从节点的offset值为100,那么主节点在进行数据传输时,仅需将101~200的数据部分传递给从节点即可。而这部分的数据所在存储的位置,就是复制积压缓冲区。
2)复制积压缓冲区
特征:
-
由主节点维护
-
固定大小,默认为1MB,可通过配置repl-backlog-size
-
先进先出(FIFO)队列
当主节点开始有从节点时创建复制积压缓冲区,其作用是备份主节点最近发送给从节点的数据。无论从节点有多少个,仅创建一个复制积压缓冲区。除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。
由于它是先进先出的队列,且大小固定,所以他只能保存主节点最近执行的写命令,时间较早的写命令会被挤出缓冲区。当主从节点的 offset 相差较大时,超出了复制积压缓冲区的范围,则无法进行部分复制,只能进行全量复制了,这时只能通过地方适当增加复制积压缓冲区的大小来保证在绝大多数中断情况下都可以使用部分复制。
从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:
- 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
- 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。
3) 运行ID(runid)
每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。通过info Server命令,可以查看节点的runid。
主从节点在初次建立连接进行全量复制时(从节点发送 psync?-1
),主节点会将自己的 runid 告知给从节点,从节点将其保存起来。当主从节点断开重连时,从节点会将这个 runid 发送给主节点,主节点会根据从节点发送的 runid 来判断选择何种复制:
-
如果从节点发送的 runid 与当前主节点的 runid 一致时,主节点则尝试进行部分复制,当然能不能进行部分复制还要看偏移量是否在复制积压缓冲区
-
如果从节点发送的 runid 与当前主节点的 runid 不一致时,则进行全量复制
除了上述的部分复制三个概念外,还需要了解psync命令的执行过程:
psync命令的执行过程图(图片来源:《Redis设计与实现》):
(1)根据状态判断发送何种psync命令:
- 如果本次操作为第一次进行主从复制,先前从未执行过slaveof或执行过slaveof no one断开主从复制,则从节点发送命令psync ? -1,向主节点请求全量复制;
- 若之前执行过slaveof,则发送命令 psync {runid} {offset},其中runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量。
(2)当主节点接收到psync请求后,根据psync命令,再结合服务器当前状态,判断执行全量复制还是部分复制:
- 如果主节点版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送sync命令执行全量复制;
- 如果主节点版本够新,且runid与从节点发送的runid相同,且从节点发送的offset之后的数据在复制积压缓冲区中都存在,则回复+CONTINUE,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可;
- 如果主节点版本够新,但是runid与从节点发送的runid不同,或从节点发送的offset之后的数据已不在复制积压缓冲区中(在队列中被挤出了),则回复+FULLRESYNC <runid> <offset>,表示要进行全量复制,其中runid表示主节点当前的runid,offset表示主节点当前的offset,从节点保存这两个值,以备使用。
五、心跳机制(命令传播阶段)
在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用。
1.主->从:PING
每隔指定的时间,主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断。
PING发送的频率由repl-ping-slave-period参数控制,单位是秒,默认值是10s。
2. 从->主:REPLCONF ACK
在命令传播阶段,从节点会向主节点发送REPLCONF ACK命令,频率是每秒1次;命令格式为:REPLCONF ACK {offset},其中offset指从节点保存的复制偏移量。REPLCONF ACK命令的作用包括:
(1)实时监测主从节点网络状态:该命令会被主节点用于复制超时的判断。此外,在主节点中使用info Replication,可以看到其从节点的状态中的lag值,代表的是主节点上次收到该REPLCONF ACK命令的时间间隔,在正常情况下,该值应该是0或1,如下图所示:
(2)检测命令丢失:从节点发送了自身的offset,主节点会与自己的offset对比,如果从节点数据缺失(如网络丢包),主节点会推送缺失的数据(这里也会利用复制积压缓冲区)。注意,offset和复制积压缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情形;区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的。
(3)辅助保证从节点的数量和延迟:Redis主节点中使用min-slaves-to-write和min-slaves-max-lag参数,来保证主节点在不安全的情况下不会执行写命令;所谓不安全,是指从节点数量太少,或延迟过高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。而这里从节点延迟值的获取,就是通过主节点接收到REPLCONF ACK命令的时间来判断的,即前面所说的info Replication中的lag值。
参考资料:
深入学习Redis(3):主从复制
Redis功能文档:复制(Replication)
Redis官网文档:复制(Replication)
《Redis实战》