分布式缓存

YangeIT大约 24 分钟高级服务框架分布式缓存Redis高级Redis持久化RDB持久化AOF持久化Redis主从Redis哨兵Redis分片集群

学习目标

  1. Redis持久化
  2. Redis主从
  3. Redis哨兵
  4. Redis分片集群

分布式缓存

单机Redis存在的问题

基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题:

image-20210725144240631
image-20210725144240631

1.Redis持久化

1.1.RDB持久化 🍐

RDB持久化

Redis有两种持久化方案:

  • RDB持久化
  • AOF持久化

RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照

简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。

快照文件称为RDB文件,默认是保存在当前运行目录。

小结

1.1.3.小结

RDB方式bgsave的基本流程?

  • fork主进程得到一个子进程,共享内存空间
  • 子进程读取内存数据并写入新的RDB文件
  • 用新RDB文件替换旧的RDB文件

RDB会在什么时候执行?save 60 1000代表什么含义?

  • 默认是服务停止时
  • 代表60秒内至少执行1000次修改则触发RDB

RDB的缺点?

  • RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险
  • fork子进程、压缩、写出RDB文件都比较耗时

1.2.AOF持久化

AOF持久化

AOF原理

AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。

image-20210725151543640
image-20210725151543640

记录指令,以及指令的长度 如$3表示长度为3

RDB与AOF对比

1.3.RDB与AOF对比

RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。

image-20210725151940515
image-20210725151940515

2.Redis主从

2.1.搭建主从架构

前言

单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。

image-20210725152037611
image-20210725152037611

具体搭建流程参考课前资料《Redis集群.md》

image-20210725152052501
image-20210725152052501

2.2.主从数据同步原理 🍐

前言

1.全量同步

主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点,流程:

image-20210725152222497
image-20210725152222497

这里有一个问题,master如何得知salve是第一次来连接呢??

有几个概念,可以作为判断依据:

  • Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
  • offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。

因此slave做数据同步,必须向master声明自己的replication id 和offset,master才可以判断到底需要同步哪些数据。

因为slave原本也是一个master,有自己的replid和offset,当第一次变成slave,与master建立连接时,发送的replid和offset是自己的replid和offset。

master判断发现slave发送来的replid与自己的不一致,说明这是一个全新的slave,就知道要做全量同步了。

master会将自己的replid和offset都发送给这个slave,slave保存这些信息。以后slave的replid就与master一致了。

因此,master判断一个节点是否是第一次同步的依据,就是看replid是否一致

如图:

image-20210725152700914
image-20210725152700914

完整流程描述:

  • slave节点请求增量同步
  • master节点判断replid,发现不一致,拒绝增量同步
  • master将完整内存数据生成RDB,发送RDB到slave
  • slave清空本地数据,加载master的RDB
  • master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
  • slave执行接收到的命令,保持与master之间的同步

代码操作

2.3.主从同步优化 🍐

主从同步优化

主从同步可以保证主从数据的一致性,非常重要。

可以从以下几个方面来优化Redis主从集群:

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。
  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力

主从从架构图:

image-20210725154405899
image-20210725154405899

小结

简述全量同步和增量同步区别?

  • 全量同步: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哨兵

3.1.哨兵原理

哨兵原理

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。

3.1.1.集群结构和作用

哨兵的结构如图:

image-20210725154528072
image-20210725154528072

哨兵的作用如下:

  • 监控:Sentinel 会不断检查您的master和slave是否按预期工作
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

代码操作

小结

  • Sentinel的三个作用是什么?

    • 监控
    • 故障转移
    • 通知
  • Sentinel如何判断一个redis实例是否健康?

    • 每隔1秒发送一次ping命令,如果超过一定时间没有相向则认为是主观下线
    • 如果大多数sentinel都认为实例主观下线,则判定服务下线
  • 故障转移步骤有哪些?

    • 首先选定一个slave作为新的master,执行slaveof no one
    • 然后让所有节点都执行slaveof 新master
    • 修改故障节点配置,添加slaveof 新master

3.2.搭建哨兵集群

搭建哨兵集群

具体搭建流程参考课前资料《Redis集群.md》

image-20210725155019276
image-20210725155019276

代码操作

3.3.RedisTemplate

前言

在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。

下面,我们通过一个测试来实现RedisTemplate集成哨兵机制。

代码操作

3.3.1.导入Demo工程

首先,我们引入课前资料提供的Demo工程:

image-20210725155124958
image-20210725155124958

4.Redis分片集群

4.1.搭建分片集群

搭建分片集群

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题

  • 高并发写的问题

使用分片集群可以解决上述问题,如图:

image-20210725155747294
image-20210725155747294

4.2.散列插槽

散列插槽

Redis插槽(Redis Slots)是用于分片的概念,它在Redis集群中用于将数据均匀分散到多个Redis节点上。每个插槽对应一个特定的数据范围,Redis使用哈希槽(Hash Slot)算法来确定键(Key)应该存储在哪个插槽中。Redis的插槽机制允许横向扩展和分布式数据存储,以提高性能和可扩展性。

每个Redis集群节点都负责维护一部分插槽,并负责处理与这些插槽相关的键。这使得Redis集群能够处理大量的数据,而不会因单一节点的容量限制而导致性能下降。

关于Redis插槽的一些要点:

  • Redis插槽的数量是固定的,默认有16384个插槽(0-16383)。
  • 插槽的分配是自动的,Redis会尝试平均分配插槽到可用的节点上。
  • 插槽的分配信息可以使用CLUSTER SLOTS命令查看。
  • Redis的插槽机制允许在集群中添加或删除节点,以进行横向扩展或缩减。
  • 添加或删除插槽的数量通常需要对Redis集群进行重新分片,这可能需要一些操作。

关于增加插槽的数量,Redis的插槽数量是在集群初始化时固定的,通常情况下不建议更改插槽的数量,因为它会涉及到对整个集群的重新分片操作,可能导致数据的迁移和集群的不稳定。

总结

Redis如何判断某个key应该在哪个实例?

  • 将16384个插槽分配到不同的实例
  • 根据key的有效部分计算哈希值,对16384取余
  • 余数作为插槽,寻找插槽所在实例即可

如何将同一类数据固定的保存在同一个Redis实例?

  • 这一类数据使用相同的有效部分,例如key都以{typeId}为前缀 image

4.3.集群伸缩

集群伸缩

redis-cli --cluster提供了很多操作集群的命令,可以通过下面方式查看:

image-20210725160138290
image-20210725160138290

比如,添加节点的命令:

image-20210725160448139
image-20210725160448139

--cluster-slave 身份定了 -- cluster-master_id 是谁的奴隶

代码操作

接下来在win11的系统下操作

2.创建新的redis实例

  1. 查看当前实例
redis-cli.exe -p 6380 cluster nodes
image
image

2.创建一个将任何一个文件夹复制一下 6386,该问:

image
image
  1. 修改配置文件:
image
image

配置文件名字,也改成6386

4.编写启动脚本---启动

image
image
image
image

目前还只有6个节点

4.4.故障转移

故障转移

集群初识状态是这样的:

image
image

其中6380,6386,6381,6382都是master,我们计划让6382宕机。

代码操作

1.自动故障转移

当集群中有一个master宕机会发生什么呢?

直接停止一个redis实例,例如6382:

直接差叼窗口 image

1)首先是该实例与其它实例失去连接

2)然后是疑似宕机:

image
image

3)最后是确定下线,自动提升一个slave为新的master:

image
image

4)当6382再次启动,就会变为一个slave节点了:

image
image

4.5.RedisTemplate访问分片集群

RedisTemplate访问分片集群

RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:

  • 1)引入redis的starter依赖
  • 2)配置分片集群地址
  • 3)配置读写分离

与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

spring:
  redis:
    cluster:
      nodes: # 配置分片集群的地址
        - 192.168.150.101:7001
        - 192.168.150.101:7002
        - 192.168.150.101:7003
        - 192.168.150.101:8001
        - 192.168.150.101:8002
        - 192.168.150.101:8003

在项目的启动类中,添加一个新的bean:

@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

面试题

  • 初级级别:

    • 什么是Redis的持久化,为什么我们需要持久化?
    • 请解释Redis的主从架构,主节点和从节点各自承担什么角色?
    • 如何进行Redis的数据备份?
    • 什么是Redis的RDB快照持久化,它的优点和缺点是什么?
    • 什么是Redis的AOF持久化,它的优点和缺点是什么?
  • 中级级别:

    • 请解释Redis主从复制的同步原理,包括全同步和增量同步。
    • 什么是Redis哨兵机制,它的作用是什么,如何配置和使用?
    • 请讨论Redis的分片集群是如何工作的,有什么优势和挑战?
    • 什么是散列插槽(Hash Slot),它在Redis集群中的作用是什么?
    • 在Redis集群中,如何进行集群的伸缩(扩展或缩减集群规模)?
  • 高级级别:

    • Redis的RDB快照和AOF持久化在哪些场景下更适合使用,如何选择合适的持久化方式?
    • 在Redis主从复制中,如果主节点宕机,会如何处理,从节点如何晋升为主节点?
    • Redis哨兵机制的工作原理是什么,如何配置多个哨兵来实现高可用性?
    • Redis集群中的数据分片和散列插槽是如何实现的,有什么注意事项?
    • 请解释Redis的数据分片策略,例如一致性哈希和哈希槽分片。