「黑马 Redis」一、分布式缓存
引入
单点 Redis 问题:
- 数据丢失问题
- 实现 Redis 数据持久化
- 并发能力问题
- 搭建主从集群,实现读写分离
- 存储能力问题
- 学习 es,搭建分片集群,利用插槽机制实现动态扩容
- 故障恢复问题
- Redis 哨兵,实现健康监测以及自动恢复
Redis 数据持久化 - 数据丢失问题
RDB 持久化
RDB:Redis Database Backup file,也称 Redis 数据快照,将内存中所有数据记录到磁盘,一旦 Redis 故障重启,就从磁盘中读取快照文件,恢复数据
快照文件称为 RDB 文件,默认保存在当前运行目录
可以利用命令手动执行命令备份,但如果是手动停机的话也会进行备份
命令 save
由主线程执行,会阻塞所有命令
命令 bgsave
后台异步备份,由子进程执行,不会影响主进程
同样 Redis 内部也有触发 RDB 的机制,在 redis.conf,save x y
意为 x 秒内,如果至少有 y 个 key 被修改,则执行 bgsave

image.png|500
对于 RDB 的自动备份,当 bgsave 开始时会 fork 主进程得到子进程,子进程利用页表共享主进程的内存数据(也就是 Redis 数据),完成 fork 后读取内存数据写入 RDB 文件

image.png|500
bgsave 的 fork 时是阻塞的,并且是利用复制过来的页表读取内存数据,如果在读取时,主进程又写入数据则会采用 copy-on-write 技术 > TODO:只复制要写的数据?怎么映射
AOF 持久化
为了弥补 RDB 的缺陷:
- RDB 执行间隔过长,则有可能两次 RDB 之间写入的数据有丢失风险
- fork 子进程、rdbcompress 压缩、写出 RDB 文件耗时、耗 cpu
AOF:Append Only File,追加文件,将每一个写命令记录在 AOF 文件中,约等于命令日志文件
格式:${下一行字符个数}\n{命令字符串}
示例 set num 12
$3
set
$3
num
$2
12
AOF 配置
注:如果启用 appendfsync
可能会因为宕机导致缓存区内的数据丢失

image.png|500
AOF 记录的是命令,往往会有很多无用命令,另外假如对一个 key 进行多次操作,只有最后一次有效,可见 AOF 文件一定会比 RDB 文件大很多,为了优化这一点,可以采用命令 bgwriteaof
,对 AOF 文件执行重写,削减无效命令,并且还会压缩命令,更加优化空间占用

image.png|500
AOF 与 RDB 对比
RDB 可以作为机房备份,AOF 因为数据更完整,所以优先 AOF 恢复

image.png|500
Redis 主从集群 - 并发能力问题
搭建主从架构
读多写少,所以读写分离,主节点负责写并同步给从节点,从节点负责读

image.png|500
开启默认的 RDB 配置,关闭 AOF,如果是一台机器部署集群,修改 port,然后各自启动
replica 的叫法从 redis5.0 后启用,之前只有 slave
开启主从关系:
- 永久:修改配置文件,添加
slaveof/replicaof <masterip> <masterport>
- 临时:在 redis-cli 中使用命令
slaveof/replicaof <masterip> <masterport>
,重启后失效
这时主从关系就已经建立,可以用 INFO REPLICATION
查看相关主从信息,主可以写、读,但是从只能读
数据同步原理
全量同步
主从第一次同步(建立连接)是全量同步

image.png|500
如何判断是否第一次:
- Replication Id;简称 replid,是数据集的标记,id 一致则说明是一个数据集,每个 master 都有一个唯一的 replid,slave 会继承 master 的 replid
- offset:偏移量,随着记录在 repl_baklog 数据增加而增大,slave 完成同步会记录当前同步的 offset,并与 master 的 offset 对比,判断数据是否落后
所以 slave 在数据同步时,需要向 master 声明自己的 replication id 以及 offset,让 master 知道要同步那些数据
增量同步
第一次是全量同步,但如果 slave 重启则进行增量同步

image.png|500
repl_baklog 类似于环形覆盖记录,offset 对应了位置,红色部分即为未同步部分
当红色部分满溢到覆盖红色部分,意味着增量同步失效,只能再次全量同步
优化同步
加快全量同步、避免全量同步、减少主节点压力

image.png|500
全量同步:master 将完整内存数据生成 RDB,发送 RDB 到 slave 后续命令则记录在 repl_baklog,逐个发送给 slave 增量同步:slave 提交自己的 replid、offset 到 master,master 返回 repl_baklog 中 offset 之后的命令
执行全量同步:slave 节点第一次连接 master 节点时;slave 节点断开时间太久,repl_baklog 中的 offset 已经被覆盖时 执行增量同步:slave 节点断开又恢复,并且在 repl_baklog 中能找到相应的 offset 时
Redis 哨兵 - 故障恢复问题
sentinel 哨兵介绍
引入:slave 宕机可以找 master 同步恢复数据,那如果 master 宕机?
sentinel 哨兵机制可以实现主从集群的故障恢复
- 监控:不断检查 master、slave
- 自动故障恢复:如果 master 故障,则会选择一个 slave 提升为 master,当故障实例恢复后也以新的 master 为主
- 通知:sentinel 充当 Redis 客户端的服务发现来源,当集群故障转移时会将新消息推送到 Redis 客户端
Sentinel 利用心跳机制监测服务状态,每隔 1s 向每隔实例发送 ping
- 主观下线:某 sentinel 节点发现某实例未在规定时间内响应,则仍未该实例主观下线
- 客观下线:超过指定数量 quorum 的 sentinel 都认为该实例主观下线,则该实例认为客观下线,quorum 值最好超过 sentinel 实例一半
选举新的 master 节点,当 master 故障时
- 判断 slave 节点与原 master 节点断开时间,超过指定值 down-after-milliseconds *10 则排除该节点
- 判断 slave 节点的 slave-priority 值,越小越优先,如果是 0 则不参与选举
- 判断 slave-priority 相同 offset 值,越大说明同步越多,优先级越高
- 最后判断 slave 节点的运行 id 大小,越小的优先级越高,运行 id 实际无意义
实现故障转移
让被选的 slave 节点 slaveof no one
成为新 master节点
让其余 slave 节点 salveof ip port
认主新 master 节点,并同步数据

image.png|500
搭建 sentinel 哨兵集群
根据 sentinel.conf 启动 redis-sentinel,均监控 master 节点,因为 master 节点 info replication 可以得知 slave 节点信息

image.png|500
RedisTemplate 哨兵模式
pom 配置
指定 master 名称要与哨兵配置中的 master 名称一致,这个名称用来标识整个主从组,哨兵监测的就是这个名称标识的主从组

image.png|500
sentinel 通知的作用,充当了 Redis 客户端的服务发现来源,所以不用配置 Redis 节点,而是让 sentinel 维护
配置读取策略

image.png|500
Redis 分片集群 - 存储能力问题
搭建 Redis 分片集群
引入:单节点内存设大了,RDB 会大,设小了,存储数据又不够
分片集群解决海量数据存储问题、高并发写问题

image.png|500
每个 master 相互心跳检测并可以充当路由进行转发,不需要哨兵
redis.conf

image.png|500
建立连接,创建集群

image.png|500
查询分片集群情况 redis-cli -p port cluster nodes
散列插槽
Redis 会把每一个 master 节点映射到 0~16383 共 16384 个插槽 hash slot 上
数据与插槽绑定,根据有效部分,{} 内的部分,无则整体,进行 CRC16 hash 后模 16384

image.png|500
在 redis 连接时,要进入集群使用
redis-cli -c -p port
,-c
代表集群模式
根据散列值根据 key 的有效部分,也就是 {} 有则 {} 内部分,无则整个 key,可以利用 {} 将一类数据定向到同一个节点
集群伸缩
获取集群模式帮助 redis-cli --cluster help
添加节点 add-node new_host:new_port existing_host:existing_port [--cluster-slave(加了就是 slave,不加默认为 master)] [--cluster-master-id id]
分配插槽 redis-cli -c reshard ip:port
后会询问要移动 1 - 多少插槽,以及接受的 nodeId,以及拷贝的数据源的 nodeId,用 done 结束
删除节点需要先清空插槽然后再删除
故障转移
当集群中有一个 master 节点宕机,他的 slave 会接管成为 master
强制停机 redis-cli -p port shutdown

image.png|500
上面是自动的故障转移,某台 master 宕机会被其他 master 心跳检测到,然后其 slave 接管成为 master
当需要一台性能更好的 Redis 实例时,可以让新实例加入集群,成为弱的 master 的 slave,随后故障接管
在 slave 节点上 CLUSTER FAILOVER
接管 master

image.png|500
RedisTemplate 访问分片集群
application.propertities 配置

image.png|500
正常的使用,不过会自动进行读写分离以及写路由