目录
因为单节点Redis他的并发能力是有上限的,但是为了更好的提高Redis的并发能力,那就要搭建主从集群,实现读写分离的目标
1.1.主从集群结构介绍
这是一个Redis主从集群结构图:
如图所示,图中的集群中有1个master节点和两个slave节点(也可以叫replica)。当我们访问主从集群通过Redis的Java客户端的时候,应该做好路由:
1.1.1 当我们写响应的操作,并且要访问Master主节点的时候,主节点会把消息同步给他相应的两个子节点slave/replace。
1.1.2 主节点是可读可写的,但是从节点只能读不能写,因此,如果要读取信息操作,最好是访问从节点slave,从而减少并发压力;
1.2.如何搭建主从集群
1.2.1 首先,我们在虚拟机中利用docker创建三个容器,这三个容器用来搭建集群,例如下图:
1.2.1 启动多个Redis实例
我们把响应的docker -compose的yaml文件放入虚拟机中,这个文件是用来搭建集群的,您所在的公司或者学校会提供,或者你再接去网上找一个,或者你直接自己创建一个,我把内容放下面了:
将这个文件放进你虚拟机的redis文件夹中并且执行以下操作运行集群:
docker compose up -d
如果出现以下结果,那就说明启动成功
这时候,我们可以通过docker -ps查看我们的容器已经正常启动了1.2.2.建立集群
1.2.2.建立集群方法:
虽然我们已经创建了三个redis实例,但是并没有让其产生主从关系,因此,我们要从让他们形成主从关系,按照下面操作:
Redis5.0以前
slaveof <masterip> <masterport>
Redis5.0以后
replicaof <masterip> <masterport>
其中有两种模式:
1. 永久生效:在redis.conf文件中利用slaveof
命令指定master
节点
2. 临时生效:直接利用redis-cli控制台输入slaveof
命令,指定master
节点
这里,我们首先来进行临时生效的测试:
先进入r2,并且认r1为主节点:
docker exec -it r2 redis-cli -p 7002
slaveof 192.168.150.101 7001
同理,进入r3,并且让r1成为master主节点:
docker exec -it r3 redis-cli -p 7003
slaveof 192.168.150.101 7001
这时候我们可以进入r1,并且查看了:
连接r1: docker exec -it r1 redis-cli -p 7001
查看集群状态 :info replication
如果成功,结果是如图:
从图中不难看出,这边的节点9001为master主节点,而他有两个slave与他连接:
分别是:9002的slave0 和9003的slave1
接下来可以进行测试,在r1,r2,r3中分别进行如下操作:
set num 123 get num
get num
我们会发现,在master中,可以写入操作,也可以读取操作,但是在两个slave中不能写操作,但是我们不难发现,我们在master中写入的操作同步到两个slave中了,而且两个slave可以读取,也验证了我们的slave节点的只读不写。也就是说,读写的操作已经分离。
1.3. 主从同步的原理
1.3.1. 全量同步
从上面的结果中我们不难看出,主节点写入的操作信息回同步到他相应的slave节点上,为此,也说明了主从之间完成了数据同步。
那么这个同步的实现,接下来回说明:
当主从第一次建立连接的时候,master和slave会执行全量同步,将master节点的所有数据都拷贝给slave节点,流程:(这个图是网络上看到的,可以借助理解)
从图中我们不难看出,会有两中同步:全量同步 以及 增量同步;
全量同步是第一次同步时,master节点会将自己的版本信息全部同步给slave节点,但是我们该怎么判断是否是第一次同步呢?
有以下几点可以作为判断依据:
1.Replication Id
:简称replid
,是数据集的标记,replid一致则是同一数据集。每个master
都有唯一的replid
,slave
则会继承master
节点的replid
2.offset
:偏移量,随着记录在repl_baklog
中的数据增多而逐渐增大。slave
完成同步时也会记录当前同步的offset
。如果slave
的offset
小于master
的offset
,说明slave
数据落后于master
,需要更新。
结合上面的,slave向master申请同步数据的第一步就是把自己slave的offset
和replication id传递给master。master会判断是否给全量还是增量同步,如果是增量同步,该同步那些
由于我在进行slaveof之前,所有的节点都为master,都有自己相对应的replid和offset,所以当执行slaveof建立主从关系的时候,发出的repid和offset是自己,肯定不与master不一样,当master接到传过来的replid和offset和自己不一样,说明是一个全新的slave,就会选择进行全量同步了,这时,master会将自己的replid和offset发给这个slave,slave会将自己的数据清空,并且把master的信息全部同步到本地,从此,slave和master就一致了。
总结:master判断一个节点是否是第一次同步,就是通过replid是和和自己的一致:
通过下图加强理解:
流程如下:
1. slave
节点请求增量同步
2. master
节点判断replid
,发现不一致,拒绝增量同步
3. master
将完整内存数据生成RDB
,发送RDB
到slave
4. slave
清空本地数据,加载master
的RDB
5. master
将RDB
期间的命令记录在repl_baklog
,并持续将log中的命令发送给slave
6. slave
执行接收到的命令,保持与master
之间的同步
1.3. 2. 增量同步
上面介绍了全量同步,那么增量同步也是相同的原理:
就是只更新slave与master存在差异的部分数据
如下图:
简单的说就是,slave不是第一次同步,就把master中新增加的增加同步到自己哪里。也可以理解为把比master少的东西全部补充拿过来;
1.3.3. repl_baklog原理
当master给slave传输数据的同时,master又写了新的操作的时候,行的操作会放进repl_baklog积压缓存区中,等master传输完后会继续把新的数据同步给slave;
repl_baklog
中会记录Redis处理过的命令及offset
,包括master当前的offset
,和slave已经拷贝到的offset
slave与master的offset之间的差异,就是salve需要增量拷贝的数据了,随着不断有数据写入,master的offset逐渐变大,slave也不断的拷贝,追赶master的offset;
一直到这个数组被填充满;
此时,如果有新的数据写入,就会覆盖数组中的旧数据。不过,旧的数据只要是绿色的,说明是已经被同步到slave的数据,即便被覆盖了也没什么影响。因为未同步的仅仅是红色部分。
但是,如果slave出现网络阻塞,导致master的offset
远远超过了slave的offset;
如果master继续写入新数据,master的offset
就会覆盖repl_baklog
中旧的数据,直到将slave现在的offset
也覆盖
棕色框中的红色部分,就是尚未同步,但是却已经被覆盖的数据。此时如果slave恢复,需要同步,却发现自己的offset
都没有了,无法完成增量同步了。只能做全量同步。
repl_baklog
大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久,导致尚未备份的数据被覆盖,则无法基于repl_baklog
做增量同步,只能再次全量同步。
1.4. 主从同步优化
为什么要主从同步:
主从同步可以保证主从数据的一致性
下面有几种方式来实现同步优化:
1. 在master中配置repl-diskless-sync yes
启用无磁盘复制,避免全量同步时的磁盘IO。
2. Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
3. 适当提高repl_baklog
的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
4. 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从
链式结构,减少master压力
主-从-从的结构图:
1.5 总结
1.5.1. 简述全量同步和增量同步区别?
1.5.1.1 全量同步:master将完整内存数据生成RDB,发送RDB到slave。后续命令则记录在repl_baklog,逐个发送给slave。
1.5.1.2.增量同步:slave提交自己的offset到master,master获取repl_baklog中从offset之后的命令给slave。
1.5.2. 什么时候执行全量同步?
1.5.2. 1. slave节点第一次连接master节点时
1.5.2. 2.slave节点断开时间太久,repl_baklog中的offset已经被覆盖时
1.5.3. 什么时候执行增量同步?
1.5.3.1. slave节点断开又恢复,并且在repl_baklog
中能找到offset时