分布式缓存

分布式缓存

文章目录

  • 分布式缓存
    • Redis持久化
      • RDB持久化
      • AOF持久化
    • Redis主从
      • 搭建主从架构
        • 准备实例与配置
        • 开启主从关系
      • 数据同步原理
        • 全量同步
        • 增量同步
    • Redis哨兵机制
      • 哨兵的作用
        • 服务状态监控
        • 故障转移
      • 搭建哨兵集群
      • RedisTemplate的哨兵模式
    • Redis分片集群
      • 搭建分片集群
        • 准备实例和配置
        • 启动实例
        • 创建集群
      • 散列插槽
      • 集群伸缩
      • 故障转移
      • 数据迁移
      • RedisTemplate访问分片集群

分布式缓存

日常测试和学习的过程中,我们都习惯用Redis单节点来进行操作。但是单节点Redis往往会暴露出一些问题。如:

  1. 数据丢失问题:Redis是内存存储,服务器一旦重启就可能会丢失数据。
  2. 并发能力问题:单节点Redis并发能力虽然不错,但也无法满足如618和双11这样的高并发场景的狂轰乱炸。
  3. 故障恢复问题:如果Redis宕机,则服务不可用,需要一种自动的故障恢复手段。然而单节点Redis一旦宕机,则整个服务将会直接瘫痪。
  4. 存储能力问题:Redis基于内存,单节点能存储的数据量难以满足海量数据需求。

所以在企业开发的过程中,通常使用的是Redis集群来构建,从而来保证Redis的高可用性和健全性。

Redis持久化

Redis中有两种持久化方式:RDB持久化、AOF持久化。

RDB持久化

Redis Database Backup file :Redis数据备份文件,也称为Redis数据快照。简单来说就是将内存中的所有数据都记录到磁盘中。当Redis实例故障重启之后,从磁盘中读取快照文件,恢复数据。快照文件称为RDB文件,默认保存在当前运行目录中。

127.0.0.1:6379> save #由Redis主进程来执行RDB,会阻塞所有命令
ok
127.0.0.1:6379> bgsave #开启子进程执行RDB,避免主进程受到影响
Background saving started

当Redis停机时会自动执行一次RDB。但如果Redis是突然宕机,而非停机,则这种RDB机制会失效。但是面对这种问题,Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:

# 900秒内,如果至少有1个key被修改,则执行bgsave
save 900 1

# 300秒内,如果至少有10个key被修改,则执行bgsave
save 300 10

# 60秒内,如果至少有10000个key被修改,则执行bgsave
save 60  10000

# 禁用RGB
save ""

RDB的其它配置也可以在redis.conf文件中设置:

# 是否压缩,建议不开启(默认开启),压缩会消耗cpu资源,cpu资源相比磁盘资源来说更为珍贵
rdbcompression yes

# RDB文件名称(可修改)
dbfilename dump.rdb

# 文件保存的路径目录
dir ./

bgsave在开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。fork采用的是copy-on-write技术:

  • 当主进程执行读操作时,访问共享内存。
  • 当主进程执行写操作时,会拷贝一份数据,执行写操作。

其基本流程(RDB方式)是:

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

分布式缓存_第1张图片

RDB存在的缺点有:

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

有什么别的持久化能弥补RDB的缺陷呢?

AOF持久化

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

分布式缓存_第2张图片

AOF默认是关闭的,需要手动去修改redis.conf配置文件来开启AOF。

# 是否开启AOF功能,默认为no
appendonly yes

# AOF文件的名称(可修改)
appendfilename "appendonly.aof"

也可以通过修改redis.conf文件来调节AOF的命令记录频率

# 标识每执行一次写命令,立即记录到AOF文件
appendfsync always

# 写命令执行完先放入AOF缓冲区,然后表示每隔1秒讲缓冲区数据写到AOF文件(默认方案)
appendfsync everysec

# 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
配置项 刷盘时机 优点 缺点
always 同步刷盘 可靠性高,几乎不丢数据 性能影响大
everysec 每秒刷盘 性能适中 最多丢失一秒数据
no 操作系统控制 性能最好 可靠性较差,可能丢失大量数据

因为AOF是靠记录命令来达到恢复数据的效果,所以AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但是只有最后一次写操作才有意义。为了解决这个问题,AOF提供了一个办法,通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到同样的效果。

image-20230530083558284

Redis会在触发阈值时自动重写AOF文件。阈值也可以在redis.conf中进行配置

# AOF文件比上次文件的大小增长超过百分之多少时则会触发重写
auto-aof-rewrite-percentage 100

# AOF文件体积超过多大以上才触发重写
auto-aof-rewrite-min-size 64mb

RDB与AOF各自都有优缺点,在实际开发中往往会结合两者一起使用。

RDB AOF
持久化方式 定时对整个内存做快照 记录每一次执行的命令
数据完整性 不完整,两次备份之间会有数据丢失 相对完整(取决于刷盘策略)
文件大小 文件体积小(有压缩) 文件体积大(记录命令)
宕机恢复速度
数据恢复优先级 低(数据完整性不如AOF) 高(数据完整性更高)
系统资源占用 高(大量的CPU和内存消耗) 低(主要是磁盘的IO资源,但在AOF重写时会占用大量的CPU和内存资源)
使用场景 追求更快的启动速度(可以容忍几分钟的数据丢失) 对数据安全性要求较高的

Redis主从

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

image-20230530091052721

搭建主从架构

如下图为Redis主从集群的基本结构

分布式缓存_第3张图片

集群中至少会包含三个节点,一个主节点,两个从节点,从而实现读写分离。

准备实例与配置

在同一台虚拟机中开启三个实例,就必须准备三份不同的配置文件和目录,配置文件所在的目录即为工作目录。

1、创建目录

创建三个文件夹,名字分别为5206、7749、8391(名字随意就好)

# 进入tmp目录
cd /tmp

# 创建目录
mkdir 5206 7749 8391

2、恢复原始配置

如果修改了redis.conf文件中的配置(没有则跳过此步骤),需要将起其中的持久化模式改为默认的RDB模式,并保持AOF为关闭状态。

# 开启RDB
# save ""(如果添加了此命令,将其注释掉)

# redis.conf文件中下面三行是默认注释,默认是保持开启状态的
save 3600 1
save 300 100
save 60 10000
 
# 关闭AOF
appendonly no

3、拷贝配置文件至每个实例目录

将redis.conf文件拷贝至刚才创建好的三个目录中(在/tmp目录下执行):

# 方式一、逐个拷贝
cp redis/redis.conf 5206
cp redis/redis.conf 7749
cp redis/redis.conf 8391

# 方式二、管道组合命令,一键拷贝
echo 5206 7749 8391 | xargs -t -n 1 cp /usr/local/redis/redis.conf

注意:redis.conf所在文件的根目录为自己redis所在的工作目录(一般为解压目录)。

4、修改每个实例的端口、工作目录

修改每个文件夹中的配置文件,将端口分别改成对应的5206、7749、8391,并将RDB文件保存位置也都修改成文件所在目录(在/tmp目录下执行)。

sed -i -e 's/6379/5206/g' -e 's/dir .\//dir \/tmp\/5206\//g' 5206/redis.conf
sed -i -e 's/6379/7749/g' -e 's/dir .\//dir \/tmp\/7749\//g' 7749/redis.conf
sed -i -e 's/6379/8391/g' -e 's/dir .\//dir \/tmp\/8391\//g' 8391/redis.conf

5、修改每个实例的声明IP

虚拟机本身有多个IP,为了避免将来混乱,我们需要在redis.conf文件中指定每一个实例的绑定ip信息,格式如下:

# redis实例的声明IP
replica-announce-ip 192.168.xxx.xxx

每个目录都需要修改,下面给出命令,一键完成修改(在工作目录下执行,修改的ip为自己虚拟机的ip)

# 逐一执行
sed -i '1a replica-announce-ip 192.168.xxx.xxx' 5206/redis.conf
sed -i '1a replica-announce-ip 192.168.xxx.xxx' 7749/redis.conf
sed -i '1a replica-announce-ip 192.168.xxx.xxx' 8391/redis.conf

# 或者一键修改
printf '%s\n' 5206 7749 8391 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.xxx.xxx' {}/redis.conf

6、启动redis

修改完成之后,开启三个redis实例(在/tmp目录下执行)

# 逐个执行
redis-server 5206/redis.conf
redis-server 7749/redis.conf
redis-server 8391/redis.conf

执行完成后,使用ps -ef | grep redis命令来查看redis服务状态。

分布式缓存_第4张图片

三个实例已经成功启动了。

开启主从关系

三个实例已经启动,但是它们之间并没有任何关系,可以使用replicaof或者slaveof( redis5.0以前 )命令。

有临时和永久两种模式:

  • 修改配置文件(永久有效):
    • 在redis.conf中添加一行配置:slaveof
  • 使用redis-cli客户端连接到redis服务,执行slaveof命令(重启后将会失效):
slaveof <masterip> <masterport>

注意:在 redis5.0 之后新增命令replicaof与salveof效果是一样的。

这里使用临时的模式来进行演示

# 在5206的实例中
redis-cli -p 5206 # 连接5206实例
slaveof 192.168.xxx.xxx 6379 # 这里的ip为主机的ip(以6379为例)

# 在7749的实例中
redis-cli -p 7749 # 连接7749实例
slaveof 192.168.xxx.xxx 6379 # 这里的ip为主机的ip(以6379为例)

# 在8391的实例中
redis-cli -p 8391 # 连接8391实例
slaveof 192.168.xxx.xxx 6379 # 这里的ip为主机的ip(以6379为例)

执行完成之后,在6379的Redis主机上输入

# 进入6379主机(已进入则忽略)
redis-cli # 或者redis-cli -p 6379

# 查看主从关系及其状态
info replication

看到如下状态,说明主从关系已经开启

分布式缓存_第5张图片

此时可以进行一些读写测试一下主从节点之间是否是数据同步的。

注意:在主节点上可以实现读写操作,而在从节点上只能进行读操作,无法进行写操作。(说明已经实现了读写分离)

image-20230530162658602


数据同步原理

全量同步

主从第一次同步为全量同步(只有第一次同步,此次同步非常消耗资源)。

分布式缓存_第6张图片

那么,master是怎么判断slave是否是第一次来同步数据呢?

通过对比replid与offset来实现

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

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

具体的流程为:

  1. slave节点请求做增量同步。
  2. master节点判断replid,发现不一致,则拒绝增量同步。
  3. master将完整的内存数据生成RDB,将RDB发送到slave(最消耗资源的步骤)。
  4. slave清空本地数据,加载master的RDB。
  5. master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave。

分布式缓存_第7张图片

增量同步

当slave重启后进行的同步为增量同步

分布式缓存_第8张图片

repl_baklog类似于一个环形数组,master记录的数据会跟着环形移动,slave也会跟着数据进行同步,当环中数据绕满一圈时,master继续存储数据,则会覆盖掉上一圈的数据,只要slave紧跟master同步,就不会出现数据无法同步的状态(左边的圆圈)。一旦slave断开时间过久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,此时只能再次做全量同步(右边圆圈)。


针对上面的问题,可以从以下几点来优化Redis主从集群:

  • 在master中配置repl_diskless_sync yes启动无磁盘复制,避免全量同步时的磁盘IO(使用前提是带宽速度够快,否则也会导致阻塞)。
  • Redis单节点上的内存占用不要过大,减少RDB导致的过多磁盘IO。
  • 适当提高repl_baklog的大小,当发现slave宕机时尽快实现故障恢复,尽可能避免全量同步。
  • 限制一个master上的slave节点数量,如果实在有太多的slave,则可以采用主-从-从链式结构(如下图),减少master压力。

分布式缓存_第9张图片


全量同步与增量同步的区别:

  • 全量同步: master将完整内存数据生成RDB,发送RDB到slave。后续命令则记录在repl_ baklog, 逐个发送给slave。
  • 增量同步: slave提交自己的offset到master, master获取
    repl_baklog 中从offset之后的命令给slave。

全量同步与增量同步执行的时机:

  • 全量同步:
    1. slave节点第一次连接master节点时。
    2. slave节点断开时间太久,repl_baklog中的offset已经被覆盖时。
  • 增量同步:slave节点断开后恢复,并且能够在repl_baklog中找到offset时。

Redis哨兵机制

从上面可以得知,如果slave宕机了,只要及时恢复,则依然可以正常工作。那如果master突然宕机了呢?要知道,在master宕机的过程中,用户就无法进行写的操作,体验性就会降低,所以就需要在slave中及时选出一个节点充当master,当之前的master恢复之后,将其变为slave即可。这样实现主从切换可以在用户毫无感觉的情况下完成,不会影响到用户体验。实现这个过程,就需要提到Redis的哨兵机制。

哨兵的作用

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

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

分布式缓存_第10张图片

服务状态监控

Sentinel基于心跳机制检测服务状态,每隔一秒向集群的每个实例发送ping命令:

  • 主观下线:如果某个Sentinel节点发现某个实例未在规定时间响应,则认为该实例主观下线(可能是网络阻塞导致)。
  • 客观下线:若超过指定数量(quorum)的Sentinel都认为该实例主观下线,则该实例客观下线(宕机)。quorum值最好超过Sentinel实例数量的一半。

分布式缓存_第11张图片

当Sentinel一旦发现master发生故障,需要按照以下选择依据从slave中选择一个作为新的master:

  1. 先判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10),则会直接排除该slave节点。
  2. 判断slave节点的slave-prority值,越小优先级越高(0则永不参与选举)。
  3. 若slave-prority值相同,则判断slave节点的offset值,越大则说明数据越新,优先级越高。
  4. 最后是判断slave节点的运行id大小,越小优先级越高。

故障转移

当选中其中一个slave作为新的master后(如slave1),故障转移步骤为:

  1. Sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master。
  2. Sentinel给所有其他slave发送slaveof 192.168.150.101 7002命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
  3. Sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点。

分布式缓存_第12张图片


搭建哨兵集群

跟创建Redis集群一样,这里创建的哨兵集群依然是以三个为一组。

在同一台虚拟机上开启三个实例,创建三个文件夹,名字为s1、s2、s3。

# 进入/tmp目录
cd /tmp
# 创建目录
mkdir s1 s2 s3

然后在s1,目录下创建一个sentinel.conf文件,并添加以下内容

# 当前Sentinel实例的端口
port 7001
# 指定当前实例ip地址(避免混乱)
sentinel announce-ip 192.168.xxx.xxx
# 指定主节点信息(mymaster为现在主节点的别名,ip为现在主节点的ip,2为选举master时的quorum值)
sentinel monitor mymaster 192.168.xxx.xxx 6379 2
# slave与master断开最长超时时间
sentinel down-after-milliseconds mymaster 5000
# slave故障恢复的超时时间
sentinel failover-timeout mymaster 60000
# 工作目录
dir "/tmp/s1"

将这个sentinel.conf文件拷贝到s2、s3两个目录中(在/tmp吗目录下执行命令)

# 方式一:逐个拷贝
cp s1/sentinel.conf s2
cp s1/sentinel.conf s2

# 方式二:管道组合命令,一键拷贝
echo s2 s3 | xargs -t -n 1 cp s1/sentinel.conf

修改s2、s3两个文件夹内的配置文件,将端口分别修改为7002、7003(在/tmp目录下执行命令)

sed -i -e 's/7001/7002/g' -e 's/s1/s2/g' s2/sentinel.conf
sed -i -e 's/7001/7003/g' -e 's/s1/s3/g' s3/sentinel.conf

修改完成后,启动这三个实例(在/tmp目录下执行命令)

# 第一个
redis-sentinel s1/sentinel.conf
# 第二个
redis-sentinel s2/sentinel.conf
# 第三个
redis-sentinel s3/sentinel.conf

启动成功之后,其他的事情就全部交给Sentinel自动完成即可。


RedisTemplate的哨兵模式

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

1、在pom.xml中引入redis的starter依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

2、在配置文件application.yml中指定sentinel相关信息

spring: 
	redis: 
		sentinel:
			master: mymaster # 指定master名称
			nodes: # 指定redis-sentinel集群信息
				- 192.168.xxx.xxx:7001
				- 192.168.xxx.xxx:7002
				- 192.168.xxx.xxx:7003
				

3、配置读写分离

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

ReadFrom是配置Redis的读取策略,是一个枚举,与以下选择:

  • MASTER:从主节点读取。
  • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica。
  • REPLICA:从slave(replica)节点读取。
  • REPLICA_PREFERRED:优先从slave(replica)节点读取,所有slave都不可用才读取master。

到此,关于RedisTemplate的哨兵模式已经配置完毕,剩余的事情就交给Sentinel自动完成。


Redis分片集群

主从架构与哨兵机制虽然可以解决高可用、读的高并发问题,但依然还有两个问题没有解决:

  1. 大量数据存储的问题
  2. 高并发写的问题

解决以上问题,可以使用分片集群。因为分片集群具有以下特征:

  • 集群中有多个master,每个master之间保存不同的数据。
  • 每个master都可以有多个slave节点。
  • master之间通过ping监测彼此之间的健康状态。
  • 客户端请求可以访问集群中任意的节点(最终都会通过路由转发到正确的节点)。

分布式缓存_第13张图片

搭建分片集群

分片集群需要的节点较多,这里使用三主三从的方式搭建一个最小型的分片集群。

分布式缓存_第14张图片

在一台虚拟机上开启6个redis实例,模拟分片集群,信息如下:

IP port 角色
192.168.xxx.xxx 5206 master
192.168.xxx.xxx 7749 master
192.168.xxx.xxx 8391 master
192.168.xxx.xxx 7001 slave
192.168.xxx.xxx 7002 slave
192.168.xxx.xxx 7003 slave

准备实例和配置

删掉之前搭建主从集群的目录,避免一些配置产生干扰

# 进入/tmp目录
cd /tmp

# 删除旧的目录
rm -rf 5206 7749 8391

# 创建目录
mkdir 5206 7749 8391 8001 8002 8003

准备一份新的redis.conf文件

port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /tmp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip(虚拟机ip)
replica-announce-ip 192.168.xxx.xxx
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /tmp/6379/run.log

将这个配置文件拷贝至每个目录下

# 进入/tmp目录
cd /tmp

# 执行拷贝
echo 5206 7749 8391 8001 8002 8003 | xargs -t -n 1 cp redis.conf

修改每个目录下的redis.conf,将其中的6379修改成对应目录名为端口。

# 进入/tmp目录
cd /tmp

# 修改配置文件
printf '%s\n' 5206 7749 8391 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf

启动实例

因为配置了后台启动模式,所以可以直接一键启动服务

# 进入/tmp目录
cd /tmp

# 一键启动所有服务
printf '%s\n' 5206 7749 8391 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

启动之后,可以查看服务是否真的已启动

ps -ef | grep redis

分布式缓存_第15张图片

如果需要关闭所有进程,则可以执行以下命令(推荐使用第二种,当第二种无法正常结束进程时使用第一种)

# 第一种
ps -ef | grep redis | awk '{print $2}' | xargs kill

# 第二种
printf '%s\n' 5206 7749 8391 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown

创建集群

服务已经全部启动,但是目前所有服务之间都是独立的,没有任何联系。

需要执行命令来创建集群(Redis5.0之前创建集群比较麻烦,但在5.0之后集群管理命令都集成到redis-cli中)。

5.0之前

Redis5.0之前集群命令都是用redis安装包下的src/redis-trib.rb来实现的。因为redis-trib.rb是用ruby语言编写的所有需要安装ruby环境

# 安装依赖
yum -y install zlib ruby rubygems
gem install redis

然后通过命令来管理集群

# 进入redia的src目录
cd /tmp/redis-6.2.4/src

# 创建集群
./redis-trib.rb create --replicas 1 192.168.xxx.xxx:5206 192.168.xxx.xxx:7749 192.168.xxx.xxx:8391 192.168.xxx.xxx:8001 192.168.xxx.xxx:8002 192.168.xxx.xxx:8003

5.0以后

集群管理已经集成到了redis-cli中

redis-cli --cluster create --cluster-replicas 1 192.168.xxx.xxx:5206 192.168.xxx.xxx:7749 192.168.xxx.xxx:8391 192.168.xxx.xxx:8001 192.168.xxx.xxx:8002 192.168.xxx.xxx:8003

输入命令之后,可以发现,redis将前三个节点作为了master,后三个作为了slave

分布式缓存_第16张图片

可以通过命令来查看集群中的状态

redis-cli -p 5206 cluster nodes

image-20230531150327626

这里如果需要打开redis命令行,可以执行以下命令

# 进入/tmp目录
cd /tmp

# 打开5206端口的redis(这里的-c就是-cluster,为集群模式)
redis-cli -c -p 5206

# 打开7749端口的redis(这里的-c就是-cluster,为集群模式)
redis-cli -c -p 7749

散列插槽

Redis会将每一个master节点映射到0~16383共16384个插槽(hash slot)上,通过查看集群信息是就可以看到

分布式缓存_第17张图片

数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:

  • key中包含"{}", 且“{}”中至少包含1个字符, “{}” 中的部分是有效部分
  • key中不包含“{}”,整个key都是有效部分

例如: key是num,那么就根据num计算,如果是{xxx}num,则根据xxx计算。计算方式是利用CRC16算法得到一
个hash值,然后对16384取余,得到的结果就是slot值。


那么Redis是如何判断某个key应该在哪个实例上的呢?

  1. 将16384个插槽分配到不同的实例
  2. 根据key的有效部分计算哈希值,并将其对16384取余
  3. 余数作为插槽,寻找插槽所在的实例。

如果将同一类数据固定存储在同一个Redis实例,应该怎么办?

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

集群伸缩

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

redis-cli --cluster help 

如下图为添加节点的命令

image-20230531152946849

重新分配插槽的命令

分布式缓存_第18张图片


故障转移

如果集群中有一个master宕机了会发生什么?

这里可以根据watch命令来实时监控集群中的变化过程(可以自行尝试手动停止master节点来模拟宕机情况)

# 进入/tmp
cd /tmp

# watch集群
watch redis-cli -p 5206 cluster nodes
  1. 首先该实例会与其他的实例失去连接。
  2. 然后被疑似宕机。
  3. 最后确定下线,自动提升一个slave作为新的master。

数据迁移

利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:

分布式缓存_第19张图片

手动的Failover支持三种不同模式(默认推荐使用缺省):

  • 缺省:默认的流程,
    如图1~6步
  • force:省略了对offset的一致性校验(忽略图中2、3步)
  • takeover:直接执行第5步,忽略数据一致性、忽略
    master状态和其它master的意见

RedisTemplate访问分片集群

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

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

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

spring: 
	redis: 
		cluster: 
			nodes: # 指定集群中的每一个节点信息
				- 192.168.xxx.xxx:5206
				- 192.168.xxx.xxx:7749
				- 192.168.xxx.xxx:8391
				- 192.168.xxx.xxx:8001
				- 192.168.xxx.xxx:8002
				- 192.168.xxx.xxx:8003

你可能感兴趣的:(redis,后端学习,分布式,缓存,redis)