1.RDB持久化
通过对数据做快照的方式来实现持久化,将内存中的数据存储到磁盘中,在数据需要恢复的时候通过快照进行恢复。
RDB持久化在四种情况下会执行:
执行save命令
执行bgsave命令
Redis停机时
触发RDB条件时
RDB的设置和使用
在redis-cli中使用RDB(在主线程中执行)
- #产生对应的RDB文件快照
- save
效果图为下:
在执行save操作时会堵塞当前的主线程,会阻塞redis当前的其他操作。
在redis-cli中使用RDB(在子线程中执行)
- #异步完成RDB操作
- bgsave
效果图为下:
设置RDB配置(redis.conf中设置)
RDB触发条件设置
- #在10秒内有100key对应的值发生改变时进行RDB
- # save "间隔" “key的修改次数”
- save 300 10
效果图为下:
RDB的其他配置
- #快照压缩,占用CPU,减少磁盘开销
-
- rdbcompression yes
-
- #设置RDB的文件,也就是生成快照的名字和使用快照恢复数据时对应的快照名
- dbfilename dump.rdb
-
效果图为下:
RDB的执行原理
redis的操作是单线程的,主线程通过页表来映射在物理内存中的对应内存块,在执行RDB的子线程中通过相同的页表来读取对应映射的内存块,从而生成对应的RDB快照。
在子线程执行读取操作时,主线程可能会同时执行增删改操作,这会导致脏读的情况,为了防止这种情况,真正的物理内存块设置了只读的权限。
在主线程中为了能够正常的进行增删改操作,通过复制对应的内存块中的数据,在该数据副本中进行增删改的操作,将页表中的关系映射到新的数据副本上。子进程中的页表不会马上更新,所以子进程的操作不会受到影响。
RDB的缺点
RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险。
fork子进程、压缩、写出RDB文件都比较耗时。
2.AOF持久化
AOF全称为Append Only File(追加文件),通过创建AOF文件将在该数据库中执行的所有指令都记录到AOF文件中,通过AOF文件对数据进行恢复。
AOF的设置和使用
AOF的开启和文件名设置
- #开启AOF
- appendonly yes
-
- #设置AOF文件名
- appendfilename "appendonly.aof"
效果图为下:
AOF的模式设置
- #总是执行,也就是在每次执行指令时就进行AOF
- appendfsync always
-
- #每过一秒执行一次AOF(默认使用策略)
- appendfsync everysec
-
- #不进行AOF,执行AOF由系统决定
- appendfsync no
对应效果图为下:
三种策略对比
AOF文件重写(文件压缩)
在redis.conf中设置AOF重写触发条件
- # AOF文件比上次文件 增长超过多少百分比则触发重写(比上次增加100触发重写)
- auto-aof-rewrite-percentage 100
- # AOF文件体积最小多大以上才触发重写 (体积大于64mb触发重写)
- auto-aof-rewrite-min-size 64mb
效果图为下:
RDB和AOF对比
docker下载redis镜像
docker pull redis
在服务器上创建对应的目录用于给redis的配置文件和持久化文件挂载。
- #集群主节点1的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node1/data
- mkdir -p /mydata/redis/cluster/node1/conf
- touch /mydata/redis/cluster/node1/conf/redis.conf
-
- #集群主节点2的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node2/data
- mkdir -p /mydata/redis/cluster/node2/conf
- touch /mydata/redis/cluster/node2/conf/redis.conf
-
- #集群主节点3的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node3/data
- mkdir -p /mydata/redis/cluster/node3/conf
- touch /mydata/redis/cluster/node3/conf/redis.conf
-
- #集群主节点4的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node4/data
- mkdir -p /mydata/redis/cluster/node4/conf
- touch /mydata/redis/cluster/node4/conf/redis.conf
-
- #集群主节点5的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node5/data
- mkdir -p /mydata/redis/cluster/node5/conf
- touch /mydata/redis/cluster/node5/conf/redis.conf
-
- #集群主节点6的data文件和conf文件映射
- mkdir -p /mydata/redis/cluster/node6/data
- mkdir -p /mydata/redis/cluster/node6/conf
- touch /mydata/redis/cluster/node6/conf/redis.conf
创建redis节点1的容器和redis节点2的容器(端口记得开放)
- #redis节点1容器
- docker create --name redis-node1 -v /mydata/redis/cluster/node1/data:/data \
- -v /mydata/redis/cluster/node1/conf/redis.conf:/etc/redis/redis.conf \
- -p 16379:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node1.conf
-
- #redis节点2容器
- docker create --name redis-node2 -v /mydata/redis/cluster/node2/data:/data \
- -v /mydata/redis/cluster/node2/conf/redis.conf:/etc/redis/redis.conf \
- -p 26379:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node2.conf
-
- #redis节点3容器
- docker create --name redis-node3 -v /mydata/redis/cluster/node3/data:/data \
- -v /mydata/redis/cluster/node3/conf/redis.conf:/etc/redis/redis.conf \
- -p 36379:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node3.conf
-
- #redis节点4容器
- docker create --name redis-node4 -v /mydata/redis/cluster/node4/data:/data \
- -v /mydata/redis/cluster/node4/conf/redis.conf:/etc/redis/redis.conf \
- -p 46379:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node4.conf
-
- #redis节点5容器
- docker create --name redis-node5 -v /mydata/redis/cluster/node5/data:/data \
- -v /mydata/redis/cluster/node5/conf/redis.conf:/etc/redis/redis.conf \
- -p 56379:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node5.conf
-
- #redis节点6容器
- docker create --name redis-node6 -v /mydata/redis/cluster/node6/data:/data \
- -v /mydata/redis/cluster/node6/conf/redis.conf:/etc/redis/redis.conf \
- -p 60000:6379 redis --cluster-enabled yes \
- --cluster-config-file redis-node6.conf
启动两个容器
docker start redis-node1 redis-node2 redis-node3 redis-node4 redis-node5 redis-node6
查看容器运行状态
docker ps
效果图为下:
设置集群关系(主从关系,最少六个节点,以上述方式创建六个节点)
分别查看redis-node1至redis-node6的IP地址。
- #查看redis节点1的信息
- docker inspect redis-node1
-
- #查看redis节点2的信息
- docker inspect redis-node2
-
- #查看redis节点3的信息
- docker inspect redis-node3
-
- #查看redis节点4的信息
- docker inspect redis-node4
-
- #查看redis节点5的信息
- docker inspect redis-node5
-
-
- #查看redis节点6的信息
- docker inspect redis-node6
-
查看的效果图为下:
redis-node1
redis-node2
redis-node3
redis-node4~6都是一样的效果。
创建集群
redis-cli --cluster create 172.17.0.3:6379 172.17.0.4:6379 172.17.0.5:6379 172.17.0.6:6379 172.17.0.7:6379 172.17.0.8:6379 --cluster-replicas 1
效果图为下:
此时三个节点是主节点,三个是从节点。
进入某个redis节点中查看集群信息
- #进入容器
- docker exec -it "id" /bin/bash
-
- #进入redis服务端
- redis-cli
-
- #查看集群情况
- cluster nodes
效果图为下:
设置主从关系就是通过 --cluster-replicas 1来控制,如果为1,也就是主节点和从节点的个数为1:1。
主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点。
Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid(用于判断数据是否为第一次同步)
offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。(判断上次数据同步到的位置)
master判断一个节点是否是第一次同步的依据,就是看replid是否一致。
在第二阶段使用的快照的数据同步,也就是RDB。
增量同步
此时数据无需完全同步,只需要从偏移量的位置向后同步到终点即可。
repl_backlog中存放数据执行的指令,在数据同步时从当前的偏移量开始的指令用于数据同步。
(有点类似AOF)
repl_backlog原理
这就要说到全量同步时的repl_baklog文件了。
这个文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。(是个闭环)
epl_baklog中会记录主节点redis中数据存储到的偏移量,也会记录从节点在epl_baklog中数据同步到的偏移量,只要二者偏移量相同就说明当前的数据已经是同步的了,如果偏移量之间还有差距说明当前数据还尚未同步。
特殊情况:从节点宕机后数据没有即使同步,主节点的偏移量越过从节点的偏移量,导致部分指令被覆盖,最终数据发生丢失。
解决方案:进行全量同步。
提供哨兵的监控来实现主从的切换,从而实现故障修复。 哨兵模式是Redis的高可用方式,哨兵节点是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。
哨兵模式的作用
1.监控: sentinel会不断的检查master和salve的运行状态的。
2.自动故障修复:sentinel会在一个master失效时将一个slave作为新的master,而在原本的master恢复后,此master就会成为slave。
3.通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端。
哨兵模式的原理
监控原理
sentinel会基于心跳监测机制,每个1秒向各个实例发送ping指令,判断各个实例的运行状态。
主观下线:当有某个sentinel发现某个实例没有在规定的时间内返回存活回应,则就会认为其下线了,这个就是主观下线。
客观下线: 当超过一定数量的sentinel发现某个实例未在规定的时间内返回存活回应,都认为其主观下线了,则就认为其为客观下线。(这里的一定数量要超过sentinel个数的二分之一)
自动故障修复
当发现某个master宕机了,sentinel就会选择一个slave作为新的master。
选择slave的步骤为下(在判断的依据相同时就会进入下一步):
1.判断slave和master断开的时长,如果超过一定的时间(down-after-milliseconds * 10) 则会将此slave排除。
2.判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举。(可以理解成权重)
3.判断slave在repl_backlog中的偏移量的大小,偏移量越大就说明更新的数据越新。
4.判断slave运行id的大小,id越小,执行的优先级越高。
自动故障修复的设置性master的步骤
1.sentinel给该slave发送指令:slaveof no one ,将slave设置为master。
2.sentinel向其他slave通知这个新的master,其他的slave都执行指令:slaveof "新masterIP" "新master的端口"
3.在原来的master恢复以后,将其设置为slave。
在做redis主从时,我们可以看到每个master都被分配了slots,这slots就master对应插槽。
自动故障转移:虽然在分片集群中没有sentinel,但在master宕机时,其会自动将slave设置为新的master,在原来的master恢复后,它会做为新的slave。
手动故障转移:
手动故障转移的步骤为下:
1.slave节点告诉master节点拒绝如何客户端的请求.
2.master返回当前的数据offset偏移量给slave,用于更新slave的蕞新数据。
3.开始进行故障转移。
4.slave标记自己为master,并将此消息广播给其他的节点。
5.原来的master做为新的slave
RedisTemplate访问分片集群(需要修改redsi.conf,保证外网也可以访问该节点)
导入依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
修改application.yml文件的配置
- spring:
- redis:
- cluster:
- nodes:
- - 132.9.214.176:16379
- - 132.9.214.176:26379
- - 132.9.214.176:36379
- - 132.9.214.176:46379
- - 132.9.214.176:56379
- - 132.9.214.176:60000
配置读写分离配置
- import io.lettuce.core.ReadFrom;
- import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- public class RedisConfiguration {
- //配置读写分离
- @Bean
- public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
- return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
- }
- }
这个bean中配置的就是读写策略,包括四种:
MASTER:从主节点读取
MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
REPLICA:从slave(replica)节点读取
REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master
测试
由于节点未开放外网访问权限,所以这里连接超时,我们只需要注释每个节点的redis.conf中 bind 127.0.0.1 即可,最终完成测试。