一、Redis高级 - 分布式缓存
-
单点Redis的问题
- 故障恢复问题:Redis宕机将导致服务不可用,需要自动故障恢复手段。
- 存储能力问题:Redis基于内存,单节点存储数据量难以满足海量数据需求。
- 并发能力问题:单节点Redis并发能力有限,无法满足高并发场景,如618。
- 数据丢失问题:Redis是内存存储,服务重启可能会丢失数据。
-
Redis持久化
-
RDB持久化:
- 简介:Redis数据备份文件,也叫Redis数据快照,把内存中所有数据记录到磁盘中,用于Redis实例故障重启后恢复数据,默认保存在当前运行目录。
- 触发方式:默认在服务停止时执行,也可在redis.conf文件中设置,如
save 900 1
表示900秒内至少有1个key被修改,则执行bgsave(开启子进程执行,避免主进程受影响)。 - 配置:在redis.conf中还可设置是否压缩(
rdbcompression yes
)、RDB文件名称(dbfilename dump.rdb
)、文件保存路径目录(dir./
)等。 - 基本流程:fork主进程得到子进程,共享内存空间;子进程读取内存数据并写入新的RDB文件;用新RDB文件替换旧的RDB文件。
- 缺点:RDB执行间隔时间长,两次RDB之间的数据可能丢失;fork子进程、压缩、写出RDB文件耗时较多。
-
AOF持久化:
- 简介:记录Redis每一个写命令的文件,可看做命令日志文件。
- 开启与配置:默认关闭,需在redis.conf中修改配置开启,如
appendonly yes
(开启AOF功能)、appendfilename "appendonly.aof"
(AOF文件名称)、appendfsync always
(每执行一次写命令立即记录到AOF文件)、appendfsync everysec
(写命令先放入AOF缓冲区,每隔1秒将缓冲区数据写到AOF文件,默认方案)、appendfsync no
(写命令放入AOF缓冲区,由操作系统决定何时写回磁盘)。 - 重写:AOF文件会比RDB文件大且记录多余命令,通过执行
bgrewriteaof
命令可重写AOF文件,用最少命令达到相同效果,Redis也会在触发阈值时自动重写,阈值可在redis.conf中配置,如auto - aof - rewrite - percentage 100
(AOF文件比上次增长超过多少百分比触发重写)、auto - aof - rewrite - min - size 64mb
(AOF文件体积最小多大以上触发重写)。 - 与RDB对比:RDB定时快照,数据完整性不完整,文件体积小,宕机恢复速度快,但数据恢复优先级低,系统资源占用高;AOF记录命令,数据完整性相对完整,文件体积大,宕机恢复速度慢,但数据恢复优先级高,系统资源占用低,AOF重写时会占用大量CPU和内存资源。
-
-
Redis主从
- 搭建主从架构:搭建主从集群实现读写分离可提高Redis并发能力,具体搭建流程参考课前资料《Redis集群.md》。
- 主从数据同步原理:
- 全量同步流程:slave节点请求增量同步;master节点判断replid,不一致则拒绝增量同步;master生成RDB并发送给slave;slave清空本地数据并加载RDB;master将RDB期间命令记录在repl_baklog并发送给slave;slave执行接收到的命令保持与master同步。
- 增量同步:slave提交offset到master,master获取repl_baklog中offset之后的命令给slave。
- 全量同步场景:slave节点第一次连接master节点;slave节点断开时间太久,repl_baklog中的offset被覆盖。
- 增量同步场景:slave节点断开又恢复,且repl_baklog中能找到offset。
- 优化:在master中配置
repl - diskless - sync yes
启用无磁盘复制避免磁盘IO;控制Redis单节点内存占用减少RDB的磁盘IO;适当提高repl_baklog大小以尽快恢复slave宕机避免全量同步;限制master上的slave节点数量,过多时可采用主 - 从 - 从链式结构减少master压力。
-
Redis哨兵
- 哨兵的作用:
- 监控:不断检查master和slave是否按预期工作。
- 自动故障恢复:master故障时将一个slave提升为master,故障实例恢复后以新master为主。
- 通知:充当Redis客户端的服务发现来源,集群故障转移时推送最新信息给客户端。
- 服务状态监控:基于心跳机制,每隔1秒向集群每个实例发送ping命令,若某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线;若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线,quorum值最好超过Sentinel实例数量的一半。
- 选举新的master:首先判断slave节点与master节点断开时间长短,超过指定值(down - after - milliseconds * 10)则排除;然后判断slave节点的slave - priority值,越小优先级越高,0则永不参与选举;若slave - prority一样,则判断slave节点的offset值,越大说明数据越新优先级越高;最后判断slave节点的运行id大小,越小优先级越高。
- 故障转移步骤:选定一个slave执行
slaveof no one
成为新master;让所有节点执行slaveof 新master
;修改故障节点执行slaveof 新master
。 - 搭建哨兵集群:具体搭建流程参考课前资料《Redis集群.md》。
- RedisTemplate的哨兵模式:
- 引入依赖:在pom文件中引入redis的starter依赖。
- 配置信息:在application.yml中指定sentinel相关信息,如
spring.redis.sentinel.master
(指定master名称)、spring.redis.sentinel.nodes
(指定redis - sentinel集群信息)。 - 配置主从读写分离:通过
ReadFrom
配置Redis读取策略,包括MASTER
(从主节点读取)、MASTER_PREFERRED
(优先从master节点读取,master不可用才读取replica)、REPLICA
(从slave节点读取)、REPLICA _PREFERRED
(优先从slave节点读取,所有slave都不可用才读取master)。
- 哨兵的作用:
-
Redis分片集群
- 搭建分片集群:可解决海量数据存储和高并发写的问题,集群中有多个master,每个master保存不同数据,每个master可有多个slave节点,master之间通过ping监测彼此健康状态,客户端请求可访问集群任意节点,最终会被转发到正确节点,具体搭建流程参考课前资料《Redis集群.md》。
- 散列插槽:Redis将每一个master节点映射到0 - 16383共16384个插槽上,数据key与插槽绑定,根据key的有效部分计算插槽值,分两种情况:key中包含“{}”且“{}”中至少包含1个字符,“{}”中的部分是有效部分;key中不包含“{}”,整个key都是有效部分,计算方式是利用CRC16算法得到hash值,然后对16384取余,结果就是slot值。
- 集群伸缩:
- 添加节点:使用
redis - cli -- cluster add - node
命令添加,如redis - cli -- cluster add - node new_host:new_port existing_host:existing_port -- cluster - slave -- cluster - master - id <arg>
。 - 删除节点:使用相应命令删除。
- 添加节点:使用
- 故障转移:集群中一个master宕机,首先与其他实例失去连接,然后疑似宕机,最后确定下线,自动提升一个slave为新的master。
- 数据迁移:利用
cluster failover
命令可手动让集群中某个master宕机,切换到执行该命令的slave节点,实现无感知的数据迁移,手动的Failover支持三种模式:缺省(默认流程)、force(省略offset一致性校验)、takeover(直接执行第5步,忽略数据一致性、master状态和其他master意见)。 - RedisTemplate访问分片集群:RedisTemplate底层基于lettuce实现分片集群支持,使用步骤与哨兵模式基本一致,分片集群配置方式在application.yml中为
spring.redis.cluster.nodes
(指定分片集群每一个节点信息)。