单点Redis的问题
数据丢失问题:Redis是内存存储,服务重启可能会丢失数据
并发能力问题:单节点Redis并发能力虽然不错,但也无法满足如618这样的高并发场景
故障恢复问题:如果Redis宕机,则服务不可用,需要一种自动的故障恢复手段
存储能力问题:Redis基于内存,单节点能存储的数据量难以满足海量数据需求
利用Redis集群解决这些:
数据丢失问题:实现Redis数据持久化。
并发能力问题搭建主从集群,实现读写分离。
故障恢复问题:利用Redis哨兵,实现健康检测和自动恢复。
存储能力问题:搭建分片集群,利用插槽机制实现动态扩容
1、Redis持久化-解决数据丢失问题
1.1 RDB持久化
是什么
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为RDB文件,默认是保存在当前运行目录。
备份如何执行的
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
Fork
- Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
1.1.1 执行时机
RDB持久化在四种情况下会执行:
①执行save命令
save时只管保存,其它不管,全部阻塞。手动保存。不建议。
②执行bgsave命令
Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求。1
③Redis停机时
Redis停机时会执行一次save命令,实现RDB持久化。若是宕机的话数据会丢失。
④触发RDB条件时
Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:
RDB的其它配置也可以在redis.conf文件中设置:
1.1.2 RDB原理
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write技术:
-
当主进程执行读操作时,访问共享内存;
-
当主进程执行写操作时,则会拷贝一份数据,执行写操作。
1.2 AOF持久化
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。禁用RDB,因为要演示AOF
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
AOF持久化流程
(1)客户端的请求写命令会被append追加到AOF缓冲区内;
(2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
(3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
(4)Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
AOF启动/修复/恢复
1.3 RDB与AOF对比
AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
2、Redis主从-解决并发能力问题
实现读写分离、性能扩展;容灾快速恢复
2.1 搭建主从架构
具体搭建流程参考课前资料《Redis集群.md》
假设有A、B两个Redis实例,如何让B作为A的slave节点?
• 在 B 节点执行命令: slaveof A 的 IP A 的 port
2.2 主从数据同步原理
2.2.1 全量同步
简述全量同步的流程?
• slave 节点请求增量同步• master 节点判断 replid ,发现不一致,拒绝增量同步• master 将完整内存数据生成 RDB ,发送 RDB 到 slave• slave 清空本地数据,加载 master 的 RDB• master 将 RDB 期间的命令记录在 repl_baklog ,并持续将 log 中的命令发送给 slave• slave 执行接收到的命令,保持与 master 之间的同步
2.2.2 增量同步
主从第一次同步是全量同步,但如果slave重启后同步,则执行增量同步
2.2.3 优化Redis主从集群
2.3 总结
简述全量同步和增量同步区别?
• 全量同步: master 将完整内存数据生成 RDB ,发送 RDB 到 slave 。后续命令则记录在 repl_baklog ,逐个发送给 slave 。• 增量同步: slave 提交自己的 offset 到 master , master 获取 repl_baklog 中从 offset 之后的命令给 slave什么时候执行全量同步?
• slave 节点第一次连接 master 节点时• slave 节点断开时间太久, repl_baklog 中的 offset 已经被覆盖时什么时候执行增量同步?
• slave 节点断开又恢复,并且在 repl_baklog 中能找到 offset 时
3、Redis哨兵-解决故障恢复问题
slave节点宕机恢复后可以找master节点同步数据,那master节点宕机怎么办?Redis哨兵可以解决
3.1 哨兵的作用和原理
哨兵如何得知每个集群中的状态呢?
3.1.1 服务状态监控
Sentinel如何判断一个redis实例是否健康?
• 每隔 1 秒发送一次 ping 命令,如果超过一定时间没有相向则认为是主观下线• 如果大多数 sentinel 都认为实例主观下线,则判定服务下线
3.1.2 选举新的master
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
• 首先会判断 slave 节点与 master 节点断开时间长短,如果超过指定值( down-after-milliseconds * 10 )则会排除该 slave 节点• 然后判断 slave 节点的 slave-priority 值,越小优先级越高,如果是 0 则永不参与选举• 如果 slave- prority 一样,则判断 slave 节点的 offset 值,越大说明数据越新,优先级越高• 最后是判断 slave 节点的运行 id 大小,越小优先级越高。
3.1.3 如何实现故障转移
故障转移步骤有哪些?
• 首先选定一个 slave 作为新的 master ,执行 slaveof no one• 然后让所有节点都执行 slaveof 新 master• 修改故障节点配置,添加 slaveof 新 master
3.2 搭建哨兵集群
《Redis集群.md》
3.3 RedisTemplate的哨兵模式
在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。
3.3.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.3.2 在application.yml中指定sentinel相关信息
spring:
redis:
sentinel:
master: mymaster # 指定master名称
nodes: # 指定redis-sentinel集群信息
- 192.168.150.101:27001
- 192.168.150.101:27002
- 192.168.150.101:27003
3.3.3 配置主从读写分离
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择:
• MASTER :从主节点读取• MASTER_PREFERRED :优先从 master 节点读取, master 不可用才读取 replica• REPLICA :从 slave ( replica )节点读取• REPLICA _PREFERRED :优先从 slave ( replica )节点读取,所有的 slave 都不可用才读取 master
4、Redis分片集群-解决存储能力问题
4.1 搭建分片集群
《Redis集群.md》
4.2 散列插槽
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:
数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:
• key 中包含 "{}" ,且“ {} ”中至少包含 1 个字符,“ {} ”中的部分是有效部分• key 中不包含“ {} ”,整个 key 都是有效部分
例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。
Redis如何判断某个key应该在哪个实例?
如何将同一类数据固定的保存在同一个Redis实例?
4.3 集群伸缩
redis-cli --cluster提供了很多操作集群的命令,可以通过下面方式查看
4.4 故障转移
自动实现
手动实现数据迁移
案例