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,保证外网也可以访问该节点)
导入依赖
org.springframework.boot
spring-boot-starter-data-redis
修改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 即可,最终完成测试。