全量复制
从节点主动找主节点进行复制
- 从节点发送
psync
命令给主节点进行数据同步,由于是第一次进行复制,从节点没有主节点的replicationid
(运行 id) 和offset
(运行偏移量),所以发送psync ? -1
?
:不知道当前的replicationid
是什么-1
:进行全量复制
- 主节点根据命令,解析出要进行全量复制,返回
+FULLRESYNC
响应 - 从节点接收主节点的运行信息进行保存(
master replicationid
…) - 主节点执行
bgsave
进行RDB
文件的持久化- 传输
AOF
也能起到数据同步的效果,但是RDB
更合适。RDB
是二进制的格式,比较节省空间。(AOF
是文本格式,更占空间) - 之后要通过网络来传输这些数据,如果文件过大,就会占据更大的网络带宽,速度也更慢
- 不能使用已有的
RDB
文件,而是必须要重新生成一下。已有的RDB
文件可能会和当前最新的数据存在较大差异
- 传输
- 主节点通过网络发送
RDB
文件给从节点,从节点保存RDB
数据到本地硬盘
第四五步都是比较耗时的,生成文件和传输文件
- 在主节点生成
RDB
文件和传输RDB
文件的过程中,还会继续收到很多新的修改操作- 新修改的数据,也必须要同步给从节点
- 当从节点收完了主节点发来的
RDB
之后,主节点就会把这些新修改的操作也发送给从节点
- 主节点将从生成
RDB
到接收完成期间执行的写命令,写入缓冲区中,等从节点保存完RDB
文件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照RDB
的二进制格式追加到收到的RDB
文件中,保持主从一致性 - 从节点清空自身原有旧数据
- 从节点加载
RDB
文件得到与主节点一致的数据 - 如果从节点加载
RDB
完成之后,并且开启了AOF
持久化功能,他会进行bgrewriteaof
操作(重写机制,给AOF
瘦身),得到最近的AOF
文件- 如果从节点已经开启了
AOF
,在上述的加载数据过程中,从节点就会产生出很多AOF
日志 - 由于当前收到的是大批量的数据,此时产生的
AOF
日志,整体来说,可能会存在一定冗余信息什么的。因此针对AOF
日志进行整理,也是必要的过程
- 如果从节点已经开启了
无硬盘模式
主节点传输给从节点数据,还要依靠 RDB
文件这个中间人,我们能不能直接将数据传给从节点,不将数据加载到 RDB
文件里面再传输呢?
主节点,在进行全量复制的时候,也支持“无硬盘模式”(diskless
)。主节点生成的 RDB
的二进制文件,不是直接保存到文件中,而是直接进行网络传输
- 省下了一系列读硬盘和写硬盘的操作
从节点之前也是先把收到的 RDB
数据,写入到硬盘中,然后再加载,现在也可以省略这个过程,直接把收到的数据进行加载了
即使引入了读硬盘模式,整个操作仍然是比较重量,比较耗时的,网络传输是没法省的
- 相比于网络传输来收,读写硬盘是小头
runId
网上的很多资料中都会出现
一个 redis
服务器上,replication id
和 run id
都是存在的,两个不同 id
看起来非常像
- 长度相同,格式也非常相似
run id
是每个节点都不相同repl id
则是具有主从关系的节点,是相同的
主从复制中,只和
replid
有关,和runid
没什么关系,runid
是在哨兵中的要点
部分复制
从节点要从主节点这里进行全量复制,但是全量复制开销是很大的。有些时候,从节点本身已经持有了主节点的绝大部分数据,这个时候,就不太需要进行全量复制了
比如:出现网络抖动,主节点这边最近修改的数据可能就无法及时同步过来了,更严重的,从节点已经感知不到主节点了(进一步的从节点可能会升级成主节点)
- 网络抖动,一般都是“暂时的”,过一会就恢复了。此时就可以让从节点和主节点重新建立联系
- 当从节点和主节点重新建立连接之后,就需要进行数据的同步
- 发送
psync
请求,psync
带有具体的replid
和offset
值。主节点就要根据psync
的参数进行判定,当前这次是按照全量复制合适还是部分复制合适
- 当主从节点之间出现网络中断时,如果超过
repl-timeout
时间,主节点会认为从节点故障并中断复制连接 - 主从连接中断期间主节点依然响应命令,但这些复制命令都因网络中断无法及时发送给从节点,所以暂时将这些命令滞留在复制积压缓冲区中
- 当主从节点网络恢复后,从节点再次连上主节点
- 从节点将之前保存的
replicationId
和offset
作为psync
的参数发送给主节点,请求进行部分复制replicationid
在描述数据的来源;offset
在描述数据复制的进度(检查是不是当初的小弟)- 如果
replicationid
不一样,就说明是个新的小弟,就得进行全量复制 - 如果
replicationid
一样,就说名是当初的小弟offset
再判定一次,看这个小弟落下的内容多不多。offset
表示从节点之前同步数据的进度是如何。主节点就看这个进度是否在积压缓冲区之内- 若在,直接进行部分复制,就只把最近这段时间的数据给复制过去即可
- 若不在,当前从节点的进度已经超出积压缓冲区的范围了,进行全量复制
- 主节点借到
psync
请求后,进行必要的验证。随后根据offset
去复制积压缓冲区查找合适的数据,并响应+CONTINUE
给从节点 - 主节点将需要从节点同步的数据发送给从节点,最终完成一致性
积压缓冲区
积压缓冲区就是一个队列,通过 info replication
命令可以看到队列的信息
- 会记录最近一段时间修改的数据,总量有限,随着时间的推移,就会把之前的旧的数据逐渐删掉
实时复制
全量复制:从节点刚连上主节点之后,进行的数据初始化工作
部分复制:全量复制的特殊情况,优化手段,目的和全量复制一样
实时复制:从节点已经和主节点同步好了数据(从节点这一时刻已经和主节点数据一致了),但是之后,主节点这边会源源不断的收到新的修改数据的请求,主节点上的数据就会随之改变,也需要能够同步给从节点
从节点和主节点之间会建立 TCP
的长连接
- 然后主节点把自己收到的修改数据的请求,通过上述连接,发送给从节点,从节点再根据这些修改请求,修改内存中的数据
- 给从节点发送数据的时候,这个过程是需要时间的
- 正常来说,这个延时较短,但是如果是多级从节点的树形结构,有很多层,延时也就会上升
在进行实时复制的时候,需要保证连接处于可用状态,此时就要用到“心跳包机制”
- 主节点:默认,每隔
10s
给从节点发送一个ping
命令,从节点收到就返回pong
- 从节点:默认,每隔
1s
就给主节点发起一个特定的请求,就会上报当前从节点复制数据的进度(offset
)
总结回顾
主从复制解决的问题:
单点问题
- 单个
redis
节点,可用性不高 - 单个
redis
节点,性能有限
主从复制的特点:
redis
通过复制功能实现主节点的多个副本- 主节点用来写,从节点用来读,这样做可以降低主节点的访问压力
- 复制支持多种拓扑结构,可以在适当的场景选择合适的拓扑结构
- 复制分为全量复制,部分复制和实施复制
- 主从节点之间通过心跳机制保证主从节点通信正常和数据一致性
主从复制配置的过程:
- 主节点配置不需要改动
- 从节点再配置文件中加入
slaveof 主节点ip 主节点端口号
的形式即可
主从复制的缺点:
- 从机多了,复制数据的延时非常明显
- 主机挂了,从机不会升级成主机,只能通过人工干预的方式恢复