Redis-高级篇(分布式缓存/持久化)
文章目录
- Redis持久化
- RDB
- AOF
- RDB 和 AOF区别
- 分布式缓存
- redis主从架构
- 数据同步
- 全量同步
- 增量同步
- 主从优化
- 哨兵模式
- 1.哨兵集群搭建
- 2.哨兵作用
- 3.服务状态监控
- 4.选举新master依据
- 5.故障转移
- 6.RedisTemplate连接哨兵模式
- 分片集群
- 1.分片集群搭建
- 2.散列插槽
- 3.集群伸缩
- 4.故障转移
- 5.手动故障转移
- 6.RedisTemplate访问分片集群
Redis持久化
RDB
RDB也叫做Redis数据快照。就是把内存中的所有数据记录到磁盘中,当Redis实例故障重启后,从磁盘中读取文件,恢复数据。快照文件默认保存在当前运行目录。
通过如下命令可以使redis执行RDB
Redis内部也有触发RDB的机制
redis.conf文件中保存触发RDB触发的条件
bgsave执行原理
主线程开辟一个子线程,将对应的页表fork进子线程,子线程从页表获取到内存物理地址,然后进行读写到磁盘文件中
页表:记录内存物理地址和逻辑地址的表,该概念来自于操作系统,所有进程操作内存都是通过页表,操作逻辑地址去操作,而不是直接读写物理内存
注意:主进程接收新的写操作,不会直接写到物理内存中(因为这样会造成子线程的脏读),所以写的时候遵循copy-on-write,会先创建一个数据B副本,将数据B拷贝入数据B副本,然后对这个副本进行写操作,RDB执行完后再将副本拷贝入内存”
极端情况下,可能RDB没有完成,所有数据都创建了副本,相当于占用了原redis存储数据的两倍的空间
RDB缺点:RDB执行有时间间隔,两次RDB之间写入数据有丢失的风险;fork子进程,压缩,写入RDB文件都比较耗时。
AOF
Redis处理的每一个命令都会记录在AOF文件中,可以看作是命令日志文件,如下图:
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF
AOF的命令记录频率也可以通过reids.conf文件配置
不同配置项的优劣
因为是记录命令,AOF文件会比RDB文件大得多,而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义,通过执行bgrewriteaof命令,可以让AOF文件执行重写操作,用最少的命令达到相同的效果。如下图
也可以在redis.conf配置达到某个阈值时自动去重AOF文件(bgrewirteaof),如下图:
RDB 和 AOF区别
分布式缓存
redis主从架构
单节点的Redis并发能力有上限,要进一步提高Redis的并发能力,需要搭建主从集群,实现读写分离,向主节点写,去从节点读,主从节点实现数据同步,结构如下图:
创建三台redis实例有很多方法,比如docker。或者创建三个目录并在其中配置好redis.conf,分别启动就可以了,具体步骤不仔细讲解了,此时三个实例还没有任何关系,要配置主从可以使用replicaof或者saveof命令,有以下两种模式:
这里启动了三个实例,然后让7002和7003端口的实例作为7001实例的从节点
左上角是操作窗口,其他三个窗口分别的7001,7002,7003的启动窗口
查看集群效果
连接7001输入INFO replication命令
两个salve(从节点)
测试
在主节和从节点上分别执行写和读操作可以执行成功,再从节点上执行写操作就报错,可知读写分离成功,如下图
为什么从节点写入失败,是因为如果从节点可以执行写入操作,那么从节点的数据永远不可能与主节点数据一致,因为主节点无法通过从节点拷贝写入的数据。
数据同步
节点之间的数据同步
主从节点如何进行数据的同步
全量同步
主从第一次同步就是全量同步,流程如下
这个流程图挺清楚的
那master怎么判断slave是不是第一次来同步数据?
这里先介绍每一个redis都会有的两个属性,判断是否是第一次关键
从redis第一次同步的时候Replication Id肯定是和主节点不一致的
所以全量同步的过程就是如下图:slave来和master同步时,会携带replid和offset,replid不一致就说明是第一次来,然后master就会返回自己的replid和offest,slave记录这两个值,以后来时都携带上和master做增量同步。
第一阶段用具体代码代替原来的说明后
增量同步
主从第一次同步是全量同步,slave后续的同步就是增量同步,过程如下图
注意:repl_baklog是一个数组,大小有上限,写满后会覆盖最早的数据,如果slave断开时间太长,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。
主从优化
- 在master中配置repl-diskless-sync yes启用无磁盘复制,就是RDB不写入I/O流中,直接写入网络中传输给slave
全量同步时,正常将RDB先写入磁盘,然后通过网络传给salve,这个配置就是直接写入网络流中,少了一次IO写磁盘操作
- Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘I/O
- 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量,如果slave实在太多,可以采用“主-从-从”的链式结构,减少master压力,如下图
即从1节点作为从2节点的主节点,这样从1节点还是可以去读
哨兵模式
Redis提供了哨兵机制来实现主从集群的自动故障恢复,哨兵结构图如下:
1.哨兵集群搭建
首先创建三个目录,然后生成三个sentinel.conf文件,这三个目录是不同sentinel运行的目录,文件配置如下图,然后把端口改成三各不一样的,把配置文件分别放入三个目录中:
先启动我们的sentinel,三个都启动
这样的话snetinel集群就已经开始检测我们的redis主从集群了
这时可以让master宕机(手动关闭),发现有一个slave变成了master,原先的master重启后变成了slave
2.哨兵作用
- 1.监控:Sentinel会不断地检查master和slave是否按期工作。
- 2.自动故障恢复:如果master故障,Sentinel会将一个slave提升为master,当故障实例恢复后也以新的master为主。
- 3.通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新的消息推送给Redis的客户端。
3.服务状态监控
Sentinel基于心跳机制检测服务状态,每隔一秒向集群的每个实例发送ping命令
- 1.主观下线:如果某个sentinel节点发现某个实例未在规定时间响应,则认为该实例主观下线。
- 2.客观下线:若规定指定数量(quorum)的sentinel都认为该实例主观下线,则认为该实例客观下线,quorum的值最好超过Sentinel实例数量的一半。
只有sentinel认为redis主从的master客观下线后,才会选举新的master节点
4.选举新master依据
一旦发现master故障,sentinel需要在slave中选择一个作为新的master,选择依据如下:
-
1.首先判断slave与master节点断开时间长短,如果超过指定值(down-after-milliseconds*10)则会排除该slave。(先排除数据过旧节点)
-
2.然后判断slave的slave-priority值,越小优先级越高,如果是0则用不参加选举。(默认都是1)
-
3.如果slave-priority值一样,则判断slave的offest值,越大说明数据越新,优先级越高。
-
4.最后是判断slave节点的运行id,越小优先级越高。
5.故障转移
当选中了其中一个slave为新的master后,故障转移步骤如下图:
sentinel给备选的slave1发送slaveof no one命令,让该节点成为master
sentinel给所有其它slave发送slaveof 192.168.150.101 7002命令,让这些slave成为新master的从节点,开始从新的master上同步数据
最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点。
6.RedisTemplate连接哨兵模式
1.在pom文件中引入redis的starter依赖,如下图:
2.在配置文件applaction.yml中指定sentinel相关信息,如下图:
3.配置主从读写分离,如下图:
ReadForm是读取策略的选择,我们选择的话就下面两种,符合我们主写从读的特征
主从和哨兵可以解决高可用,高并发读的问题,但是依然没有解决以下两个问题:
- 1.海量数据存储问题。
- 2.高并发写的问题。
分片集群
该集群方式和上面集群方式是互斥的,或者说包含,同时包含了主从和哨兵机制
但是一般很大的QPS才会用这种集群方式估计
Redis分片集群如下
每个master保存不同数据;master之间通过ping检测批次的健康状态;(这里就不需要哨兵,master之间的ping代替了sentinel,这样的话如果多个master认为主观下线,也会客观下线,然后将salve变为新的master)
每个master可以有多个salve从节点
客户端请求可以访问集群任意节点,最终都会被准发到正确节点。
1.分片集群搭建
创建六个目录,配置文件如下图,端口号要设置成六个不同的,然后分别拷贝到六个目录中
将六个Redis都启动,此时这六个实例没有任何联系,我么要创建集群,如下图
结果图,代表分片集群启动成功
通过该命令查询集群信息
2.散列插槽
插槽的作用规定了,每个key存储到哪一个redis(插槽)
Redis会把0~16383共16384个插槽(hash slot)映射到每个master上,查看集群信息时就能看到,如下图:
数据key不是与节点绑定,而是与插槽绑定
。Redis会根据key的有效部分通过CRC16算法得到一个hash值,然后会16384取余,得到的结果就是slot值
有效部分分两种情况:
- 1.key中包含"{",且“{}”中至少包含1个字符,“{}”中的部分是有效部分。
- 2.key中不包含“{}”,整个key都是有效部分。
演示以证
{a}和a都到了7003中,这里是在7001中操作本来,如果你set a 1
key为1,这样的话重定向到了我们的7003(插槽在7003)
如何将一类数据固定保存在一个redis的master
应为你重定向也要消耗网络资源
3.集群伸缩
集群伸缩即向分片集群中新增删除节点
在集群中增加一个master,并为其分配插槽。
首先启动一个新的Redis实例,配置文件中设置其端口为7004,执行如下add-node命令将该实例作为master放入集群中,如下图
此时的7004还没有分配插槽,需要我们手动分配
查询集群节点状态可以看到:
使用reshard命令将7001中的部分插槽分配给7004:
分配7001所在集群的散列插槽
指定接收3000个散列插槽,指定7004的节点id未接收散列插槽的节点:
这里source node #1是散列插槽的来源节点id
下一个填入done代表执行,也可以填另一个节点id,相当于接收多个节点的散列插槽
4.故障转移
集群最开始各个节点状态如下图:
当让7002master停机后,发现7002的从节点8003从slave变成了master,如下图:
当再次启动7002后发现,7002为slave,说明Redis的分片集群有自动的故障恢复功能,如下图
5.手动故障转移
此时我们有一个新的Redis,该Redis性能比较好,需要把原来的master替换掉,这种场景就可以使用本小结的命令来执行,让其成为master的slave,然后把master替换掉。
在slave上执行cluster failover
命令手动该slave对应的master宕机,让salve变为master,实现无感知的数据迁移,执行流程如下图:
手动的failover支持三种模式:
-
1.缺省:默认的流程,如图1~6步。
-
2.force:省略了对offset的一致性校验。
-
3.takeover:直接执行第5步,忽略数据一致性、忽略master状态和其它master的意见。
举例:7002此时是slave,如下图:
当执行failover命令后,7002对应的8003master变为slave,7002则变为master,如下图:
6.RedisTemplate访问分片集群
-
1.引入Redis的starter依赖。
-
2.配置分片集群配置。
-
3.配置读写分离。
-
以上操作中,只有配置分片集群配置与哨兵机制不同,其他完全一致,配置如下图: