Redis高级之分布式缓存

Redis高级之分布式缓存

1.单点Redis的问题

image-20240115120808799

2.Redis持久化

Redis 提供了不同级别的持久化方式:

  • RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储
  • AOF持久化方式记录每次对服务器的写操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
  • 你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整

2.1RDB

Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 指定的时间间隔内至少发生指定次数的写操作,则触发RDB后台持久化
save <seconds> <changes>
# 关闭RDB持久化的方式
save ""
# 默认Redis的RDB持久化规则设置
# After 3600 seconds (an hour) if at least 1 key changed
# After 300 seconds (5 minutes) if at least 100 keys changed
# After 60 seconds if at least 10000 keys changed
save 3600 1
save 300 100
save 60 10000

# 是否开启RDB文件压缩,默认开启,如果想要节省一些CPU资源可以关闭该选项
rdbcompression yes

# RDB文件的名称
dbfilename dump.rdb

# RDB文件的存放目录(AOF文件同样)
dir ./

image-20240116193321614

你也可以通过调用SAVE或者BGSAVE,手动让Redis进行数据保存操作。

image-20240115172902758

RDB的优点

  • RDB是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以在每个小时保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。

RDB的缺点

  • 如果你希望在Redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),但Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据
  • RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

2.2AOF

快照功能并不是非常耐久(durable):如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入且仍未保存到快照中的那些数据。从1.1版本开始,Redis增加了一种完全耐久的持久化方式:AOF持久化

img

AOF全称为Append Only File,Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。从现在开始,每当Redis执行一个改变数据集的命令时(比如SET),这个命令就会被追加到AOF文件的末尾。这样的话,当Redis重启时,程序就可以通过重新执行AOF文件中的命令来达到重建数据集的目的。

image-20240116194925765

image-20240115173124670

因为AOF的运作方式是不断地将命令追加到文件的末尾,所以随着写入命令的不断增加,AOF文件的体积也会变得越来越大。举个例子,如果你对一个计数器调用了100次INCR,那么仅仅是为了保存这个计数器的当前值,AOF文件就需要使用100条记录(entry)。然而在实际上,只使用一条SET命令已经足以保存计数器的当前值了,其余99条记录实际上都是多余的。

为了处理这种情况,Redis支持一种有趣的特性:可以在不打断服务客户端的情况下,对AOF文件进行重建(rebuild)。执行bgwriteaof命令,Redis将生成一个新的AOF文件,这个文件包含重建当前数据集所需的最少命令。Redis2.2需要自己手动执行bgwriteaof命令;Redis2.4则可以自动触发AOF重写。

触发阈值可以在配置文件redis.conf中指定:

1
2
3
4
# AOF文件比上次文件增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100
# AOF文件体积最小多少以上才触发重写
auto-aof-rewrite-min-size 64mb

2.3混合持久化

尽管RDB比AOF的数据恢复速度快,但是快照的频率不好把握:

  • 如果频率太低,两次快照间一旦服务器发生宕机,就可能会比较多的数据丢失;
  • 如果频率太高,频繁写入磁盘和创建子进程会带来额外的性能开销。

那有没有什么方法不仅有RDB恢复速度快的优点,又有AOF丢失数据少的优点呢?

当然有,那就是将RDB和AOF合体使用,这个方法是在Redis 4.0提出的,该方法叫混合使用AOF日志和内存快照,也叫混合持久化。

如果想要开启混合持久化功能,可以在Redis配置文件将下面这个配置项设置成yes:

1
aof-use-rdb-preamble yes

混合持久化工作在AOF日志重写过程。

当开启了混合持久化时,在AOF重写日志时,fork出来的重写子进程会先将与主进程共享的内存数据以RDB方式写入到AOF文件,然后主进程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以AOF方式写入到AOF文件,写入完成后通知主进程将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。

也就是说,使用了混合持久化,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。这样的好处在于,重启Redis加载数据的时候,由于前半部分是RDB内容,这样加载的时候速度会很快。加载完RDB的内容后,才会加载后半部分的AOF内容,这里的内容是Redis后台子进程重写AOF期间,主进程处理的操作命令,可以使得数据更少的丢失。

2.4大Key对持久化的影响

AOF重写机制和RDB快照(bgsave命令)的过程,都会分别通过fork函数创建一个子进程来处理任务。会有两个阶段会导致阻塞父进程:

  • 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
  • 创建完子进程后,如果父进程修改了共享数据中的大Key,就会发生写时复制,这期间会拷贝物理内存,由于大Key占用的物理内存会很大,那么在复制物理内存这一过程,就会比较耗时,所以有可能会阻塞父进程。

大Key除了会影响持久化之外,还会有以下的影响:

  • 客户端超时阻塞。由于Redis执行命令是单线程处理,然后在操作大Key时会比较耗时,那么就会阻塞Redis,从客户端这一视角看,就是很久很久都没有响应。
  • 引发网络阻塞。每次获取大Key产生的网络流量较大,如果一个Key的大小是1MB,每秒访问量为1000,那么每秒会产生1000MB的流量,这对于普通千兆网卡的服务器来说是灾难性的。
  • 阻塞工作线程。如果使用del删除大Key时,会阻塞工作线程,这样就没办法处理后续的命令。
  • 内存分布不均。集群模型在slot分片均匀情况下,会出现数据和查询倾斜情况,部分有大Key的Redis节点占用内存多。

2.5总结

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

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

有很多用户都只使用AOF持久化,但并不推荐这种方式:因为定时生成RDB快照(snapshot)非常便于进行数据库备份,并且RDB恢复数据集的速度也要比AOF恢复的速度要快。

3.Redis主从架构

3.1搭建主从集群

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

image-20240116201712297

我们开始搭建一主两从的Redis集群:

(1)创建目录

我们创建三个文件夹,名字分别叫7001、7002、7003:

1
2
3
4
# 进入/tmp目录
cd /tmp
# 创建目录
mkdir 7001 7002 7003

(2)恢复原始配置

修改redis-6.2.4/redis.conf文件,将其中的持久化模式改为默认的RDB模式,AOF保持关闭状态:

1
2
3
4
5
6
7
8
# 开启RDB
# save ""
save 3600 1
save 300 100
save 60 10000

# 关闭AOF
appendonly no

(3)拷贝配置文件到每个实例目录

然后将redis-6.2.4/redis.conf文件拷贝到三个目录中(在/tmp目录执行下列命令):

1
2
3
4
5
6
7
# 方式一:逐个拷贝
cp ../redis-6.2.4/redis.conf 7001
cp ../redis-6.2.4/redis.conf 7002
cp ../redis-6.2.4/redis.conf 7003

# 方式二:管道组合命令,一键拷贝
echo 7001 7002 7003 | xargs -t -n 1 cp ../redis-6.2.4/redis.conf

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

修改每个文件夹内的配置文件,将端口分别修改为7001、7002、7003,将RDB文件保存位置都修改为自己所在目录(在/tmp目录执行下列命令):

1
2
3
sed -i -e 's/6379/7001/g' -e 's/dir .\//dir \/temp\/7001\//g' 7001/redis.conf
sed -i -e 's/6379/7002/g' -e 's/dir .\//dir \/temp\/7002\//g' 7002/redis.conf
sed -i -e 's/6379/7003/g' -e 's/dir .\//dir \/temp\/7003\//g' 7003/redis.conf

(5)修改每个实例的声明IP

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

1
2
# redis实例的声明IP
replica-announce-ip 192.168.139.166

每个目录都要改,我们一键完成修改(在/tmp目录执行下列命令):

1
2
3
4
5
6
7
# 逐一执行
sed -i '1a replica-announce-ip 192.168.139.166' 7001/redis.conf
sed -i '1a replica-announce-ip 192.168.139.166' 7002/redis.conf
sed -i '1a replica-announce-ip 192.168.139.166' 7003/redis.conf

# 或者一键修改
printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.139.166' {}/redis.conf

image-20240117125320736

现在三个实例还没有任何关系,要配置主从可以使用replicaof或者slaveof(5.0以前)命令。

有临时和永久两种模式:

  • 修改配置文件(永久生效)

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

    1
    slaveof <masterip> <masterport>

注意:在5.0以后新增命令replicaof,与salveof效果一致。

image-20240117130026291

3.2数据同步原理

主从同步之间的第一次同步属于全量同步,此时Master节点需要生成内存数据的RDB快照文件并发放给Slave节点,由于生成RDB文件是子进程异步完成的,主进程依然正常处理客户端请求,因此主从全量同步期间Master节点可能会有新数据的写入,这时Master节点需要一块内存缓冲区repl_backlog记录所有写入命令,并增量同步给从节点(重放命令实现落库)。

image-20240117131844958

Master如何判断Slave是不是第一次来同步数据?这里会用到两个很重要的概念:

  • Replication Id:简称replid,是数据集的标记,replid一致则说明是同一数据集。每一个Master都有唯一的replid,Slave则会继承Master节点的replid。
  • offset:偏移量,随着记录在repl_backlog中的数据增多而逐渐增大。Slave完成同步时也会记录当前同步的offset,如果Slave的offset小于Master的offset,说明Slave数据落后于Master,需要更新。因此Slave做数据同步,必须向Master声明自己的replication id和offset,Master才可以判断到底需要同步哪些数据。

image-20240117132212741

image-20240117133808094

image-20240117134118872

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

  1. 在Master中配置repl-diskless-sync yes(适合于磁盘慢网络带宽高的情况)启用无磁盘复制,避免全量同步时的磁盘IO。
  2. Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO。
  3. 适当提高repl_backlog的大小,发现Slave宕机时尽快实现故障恢复,尽可能避免全量同步
  4. 限制一个Master上的Slave节点数量,如果实在是太多Slave,则可以采用主-从-从链式结构,减少Master压力。

image-20240117134810087

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

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

什么时候执行全量同步?

  • Slave节点第一次连接Master节点时。
  • Slave节点断开时间太久,repl_baklog中的offset已经被覆盖时

什么时候执行增量同步?

  • Slave节点断开又恢复,并且在repl_baklog中能找到offset时

4.Redis哨兵机制

4.1哨兵的原理

image-20240117140659819

image-20240117140835307

一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:

  • 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds*10)则会排除该slave节点;
  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举;
  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高;
  • 最后是判断slave节点运行id大小,越小优先级越高。

image-20240117141403062

经过前面一系列的操作后,哨兵集群终于完成主从切换的工作,那么新主节点的信息要如何通知给客户端呢?这主要通过Redis的发布/订阅机制来实现的。每个哨兵节点提供发布/订阅机制,客户端可以从哨兵订阅消息。

哨兵提供的消息订阅频道有很多,不同频道包含了主从节点切换过程中的不同关键事件,几个常见的事件如下:

img

客户端和哨兵建立连接后,客户端会订阅哨兵提供的频道。主从切换完成后,哨兵就会向+switch-master频道发布新主节点的IP地址和端口的消息,这个时候客户端就可以收到这条信息,然后用这里面的新主节点的IP地址和端口进行通信了。通过发布/订阅机制机制,有了这些事件通知,客户端不仅可以在主从切换后得到新主节点的连接信息,还可以监控到主从节点切换过程中发生的各个重要事件。这样,客户端就可以知道主从切换进行到哪一步了,有助于了解切换进度。

4.2搭建哨兵集群

(1)要在同一台虚拟机开启3个实例,必须准备三份不同的配置文件和目录,配置文件所在目录也就是工作目录。我们创建三个文件夹,名字分别叫s1、s2、s3:

1
2
3
4
# 进入/temp目录
cd /temp
# 创建目录
mkdir s1 s2 s3

(2)然后我们在s1目录创建一个sentinel.conf文件,添加下面的内容:

1
2
3
4
5
6
port 27001
sentinel announce-ip 192.168.139.166
sentinel monitor mymaster 192.168.139.166 7001 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/temp/s1"
  • port 27001:是当前sentinel实例的端口
  • sentinel monitor mymaster 192.168.150.101 7001 2:指定主节点信息
    • mymaster:主节点名称,自定义,任意写
    • 192.168.150.101 7001:主节点的ip和端口
    • 2:选举master时的quorum值

(3)然后将s1/sentinel.conf文件拷贝到s2、s3两个目录中(在/temp目录执行下列命令):

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

(4)修改s2、s3两个文件夹内的配置文件,将端口分别修改为27002、27003:

1
2
sed -i -e 's/27001/27002/g' -e 's/s1/s2/g' s2/sentinel.conf
sed -i -e 's/27001/27003/g' -e 's/s1/s3/g' s3/sentinel.conf

(5)为了方便查看日志,我们打开3个ssh窗口,分别启动3个redis实例,启动命令:

image-20240117143848991

(6)测试主节点宕机,查看故障恢复的日志:

image-20240117145042289

image-20240117145445174

image-20240117145804462

(7)再次恢复原来的故障主节点:

image-20240117150117762

4.3RedisTemplate的哨兵模式

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

(1)在pom文件中引入redis的starter依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

(2)然后在配置文件application.yml中指定sentinel相关信息:

1
2
3
4
5
6
7
8
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)配置主从读写分离:

1
2
3
4
@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

4.4脑裂问题

在Redis主从架构中,部署方式一般是「一主多从」,主节点提供写操作,从节点提供读操作。

如果主节点的网络突然发生了问题,它与所有的从节点都失联了,但是此时的主节点和客户端的网络是正常的,这个客户端并不知道Redis内部已经出现了问题,还在照样的向这个失联的主节点写数据,此时这些数据被主节点缓存到了缓冲区里,因为主从节点之间的网络问题,这些数据都是无法同步给从节点的。这时,哨兵也发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在从节点中选举出一个leeder作为主节点,这时集群就有两个主节点了 —— 脑裂出现了。

这时候网络突然好了,哨兵因为之前已经选举出一个新主节点了,它就会把旧主节点降级为从节点,然后从节点会向新主节点请求数据同步,因为第一次同步是全量同步的方式,此时的从节点会清空掉自己本地的数据,然后再做全量同步。所以,之前客户端写入的数据就会丢失了,也就是集群产生脑裂数据丢失的问题。

总结一句话就是:由于网络问题,集群节点之间失去联系。主从数据不同步;重新平衡选举,产生两个主服务。等网络恢复,旧主节点会降级为从节点,再与新主节点进行同步复制的时候,由于会从节点会清空自己的缓冲区,所以导致之前客户端写入的数据丢失了。

减少脑裂的数据丢的方案:

当主节点发现「从节点下线的数量太多」,或者「网络延迟太大」的时候,那么主节点会禁止写操作,直接把错误返回给客户端。

在Redis的配置文件中有两个参数我们可以设置:

  • min-slaves-to-write x,主节点必须要有至少x个从节点连接,如果小于这个数,主节点会禁止写数据。
  • min-slaves-max-lag x,主从数据复制和同步的延迟不能超过x秒,如果主从同步的延迟超过x秒,主节点会禁止写数据。

我们可以把min-slaves-to-write和min-slaves-max-lag这两个配置项搭配起来使用,分别给它们设置一定的阈值,假设为N和T。这两个配置项组合后的要求是,主节点连接的从节点中至少有N个从节点,并且主节点进行数据复制时的ACK消息延迟不能超过T秒,否则,主节点就不会再接收客户端的写请求了。

即使原主节点是假故障,它在假故障期间也无法响应哨兵心跳,也不能和从节点进行同步,自然也就无法和从节点进行ACK确认了。这样一来min-slaves-to-write和min-slaves-max-lag的组合要求就无法得到满足,原主节点就会被限制接收客户端写请求,客户端也就不能在原主节点中写入新数据了。

5.Redis分片集群

5.1搭建分片集群

image-20240117153053727

分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含3个master节点,每个master包含一个slave节点,这里我们会在同一台虚拟机中开启6个redis实例,模拟分片集群,信息如下:

IP PORT 角色
192.168.139.166 7001 master
192.168.139.166 7002 master
192.168.139.166 7003 master
192.168.139.166 8001 slave
192.168.139.166 8002 slave
192.168.139.166 8003 slave

(1)删除之前的7001、7002、7003这几个目录,重新创建出7001、7002、7003、8001、8002、8003目录:

1
2
3
4
5
6
# 进入/temp目录
cd /temp
# 删除旧的,避免配置干扰
rm -rf 7001 7002 7003
# 创建目录
mkdir 7001 7002 7003 8001 8002 8003

(2)在/temp下准备一个新的redis.conf文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /temp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /temp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.139.166
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /temp/6379/run.log

(3)将这个文件拷贝到每个目录下:

1
2
3
4
# 进入/temp目录
cd /temp
# 执行拷贝
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf

(4)修改每个目录下的redis.conf,将其中的6379修改为与所在目录一致:

1
2
3
4
# 进入/tmp目录
cd /tmp
# 修改配置文件
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf

(5)因为已经配置了后台启动模式,所以可以直接启动服务:

1
2
3
4
# 进入/tmp目录
cd /tmp
# 一键启动所有服务
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

(6)如果要关闭所有进程,可以执行命令:

1
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown

image-20240117153900797

(7)虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。我们需要执行命令来创建集群,在Redis5.0之前创建集群比较麻烦,5.0之后集群管理命令都集成到了redis-cli中。

1
redis-cli --cluster create --cluster-replicas 1 192.168.139.166:7001 192.168.139.166:7002 192.168.139.166:7003 192.168.139.166:8001 192.168.139.166:8002 192.168.139.166:8003
  • redis-cli --cluster:代表集群操作命令
  • create:代表是创建集群
  • --replicas 1或者--cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master

image-20240117154326044

image-20240117154346178

(8)通过命令可以查看集群状态:

1
redis-cli -p 7001 cluster nodes

image-20240117154513022

5.2散列插槽

Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:

image-20240117163539205

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

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

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

image-20240117163804969

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

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

5.3集群伸缩

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

image-20240117164053072


需求:向集群中添加一个新的master节点,并向其中存储key1=value1


image-20240117164745470

image-20240117165009327

可以看到,此时加入集群的7004主节点还没有任何插槽的分配,我们需要把key1所对应的插槽(9189,在7002主节点上)迁移到7004主节点上。

image-20240117170145340

image-20240117170339113 此时key1所在的插槽已经转移到7004主节点上了。

image-20240117170530321

5.4故障转移

当集群中有一个master宕机会发生什么呢?(自动故障转移)

  1. 首先是该实例与其它实例失去连接
  2. 然后是疑似宕机
  3. 最后是确定下线,自动提升一个slave为新的master

image-20240117170935700

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

image-20240117171116293


需求:在7001这个slave节点执行手动故障转移,重新夺回master地位


image-20240117171456648

5.5RedisTemplate访问分片集群

image-20240117171542874