SpringCloud(13)— 分布式缓存(Redis集群)

news2024/11/27 6:20:33

分布式缓存(Redis集群)

前言 单节点Redis的问题

1.数据丢失

Redis基于内存存储,服务器重启可能会导致数据丢失

在这里插入图片描述

2.并发能力

单节点Redis的并发能力虽然已经很不错,但是依然无法满足大型的高并发场景

在这里插入图片描述

3.故障恢复

如果Redis宕机,则服务将不可用。因此需要一种自动的故障恢复手段
在这里插入图片描述

4.存储能力

Redis基于内存,单节点存储的数据量有限,难以满足海量数据的需求

在这里插入图片描述

5.解决Redis问题的方案

在这里插入图片描述

一 搭建单节点Redis

1.使用安装包搭建单节点

先下载 Redisan安装包,笔者编辑此文档时,最新版为redis-6.0.6.tar.gz,下载完成之后将安装包放置在虚拟机或服务器的指定位置下。

#1.安装依赖
yum install -y gcc tcl
#2.解压文件
tar -xvf redis-6.0.6.tar.gz
#3.进入redis文件目录
cd /redis-6.0.6
#4.编译安装
make && make install

安装过程中出现以下错误时,可以按照以下方案解决

‘struct redisServer’ has no member named ‘sentinel_mode’
         if (server.sentinel_mode && configfile && *configfile == '-')

解决方案:Redis: 部署redis6.0.6 出现问题_天黑请赶路的博客-CSDN博客

处理完成以后,重新运行make && make install命令即可完成安装

2.Docker搭建单节点 Redis

使用Docker创建一个Redis容器并且运行即可

docker run --name myredis \
            -v /usr/local/redis:/usr/local/etc/redis \
            -p 6379:6379 \
            -d redis:latest \
            redis-server /usr/local/etc/redis/redis.conf
  • -v:挂载自己的redis配置文件目录到容器指定目录
  • -p:对外映射访问端口
  • redis-server /usr/local/etc/redis/redis.conf:指定要使用的redis.conf

更多用法参考DockerHub:redis - Official Image | Docker Hub

踩坑点:redis.conf文件需要手动创建

Redis及配置文件下载地址:redis下载 – Redis中国用户组(CRUG)

二 Redis持久化

1.RDB持久化

1.RDB基础

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

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

2.RDB存储

RDB存储方式默认由Redis开启

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

通过 save命令来实现RDB持久化,该命令由Redis主进程来执行,会阻塞所有命令。

使用save保存数据,当数据过大时,会导致其他线程无法访问Redis,缺点尤为明显

这里推荐使用bgsave命令,使用后台保存命令。该命令是在后台异步执行的,且由子进程执行RDB,不会造成主进程的阻塞。

在这里插入图片描述

注意:Redis停机时会自动执行一次RDB

Redis内部有触发RDB的机制,可以在 redis.conf 文件夹中找到。其中关于RDB的默认配置如下:

# 含义:900s内如果有1个key被修改,则执行 bgsave,其他行同理。
# 如果是 save "" 则表示删除所有的保存点。
# 禁用所有 save 表示完全禁用 RDB
#   save ""
save 900 1
save 300 10
save 60 10000

#其他配置信息
#是否压缩
rdbcompression yes
#是否校验和
rdbchecksum yes
#压缩文件名称
dbfilename dump.rdb
#是否删除非持久性实例中的rdb文件
rdb-del-sync-files no
#RDB文件保存位置,默认为当前目录
dir ./

3.RDB原理

bgsave命令开始执行时,会 fork 主进程得到子进程,子进程共享主进程的内存数据。

完成 fork 后读取内存数据并写入RDB文件中。

在这里插入图片描述

fork 底层采用了 copy-on-write 技术:

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

整体流程:

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

RDB的缺点:

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

2.AOF持久化

1.AOF基础

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

在这里插入图片描述

2.AOF存储

AOF默认是关闭状态,需要修改redis.conf配置文件开开启AOF

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

#AOF文件默认名称
appendfilename "appendonly.aof"

# AOF文件记录频率,Redis提供了三种方案,分别是 always,everysec,no
appendfsync everysec
  • everysec:写命令执行完先放入缓冲区,然后每隔1秒将缓冲区的数据写到AOF文件,是默认方案。牺牲一定的可靠性,但性能较好
  • no:写命令执行完先放入缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。性能最好,但是安全性最差
  • always:每执行一次写命令,立即记录到AOF文件,数据得到绝对的安全保障,但是性能最差
    在这里插入图片描述

3.存储示例

注意:RedisRDBAOF可以共同存在,一起保障数据的安全

修改redis.conf的关键配置如下:

# 注释掉所有 save 行,开启 AOF 存储
appendonly yes

重启Redis服务,此时默认目录下会生成 appendonlydir 文件,真对于 redis 的操作记录,全都记录在这个文件夹中。

在这里插入图片描述

# 进入 redis 容器内部
docker exec -it myredis bash
# 查看当前目录下的文件
ls
# 利用redis-cli工具操作redis,并且存储一个String数据
set name tony;

其中 /appendonlydir/appendonly.aof.1.incr.aof中就保存着我们操作redis的记录。

使用 cat 命令查看文件内容

# 切换至 appendonlydir 目录下
cd /appendonlydir
# 查看文件内容
cat appendonly.aof.1.incr.aof

在这里插入图片描述

${num}表示输入命令的字符串长度,例如 $6 表示输入了一个长度为 6 的信息。

以上文件内容表示执行了以下Redis 命令:

# 选择 0 号库
select 0
# 存储一个key=name,value=tony的字符串数据
set name tony

因为是记录命令,AOF文件会比RDB文件大很多。而且对同一个key的多次操作,只有最后一次才有意义。这样一来就存储了很多无用的数据。

通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同的效果。

bgrewriteaof

自动重写功能默认为开启状态,我们可以通过配置文件,设置触发自动重写的条件:

# 相比上次文件大小,超过指定百分比则触发重写
auto-aof-rewrite-percentage 100
# aof文件的大小达到指定大小时开始触发重写
auto-aof-rewrite-min-size 64mb

3.持久化模式对比

在这里插入图片描述

实际生产环境下往往采用两者结合的方式来使用。

三 Redis主从

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

在这里插入图片描述

在一台服务器上开启多个端口,模拟实现搭建Redis主从架构。

1.搭建Redis主从架构

创建3个文件目录,使得它们分别去运行,彼此之间互不干扰

mkdir 7001 7002 7003

准备一份原始的redis配置文件redis.conf,然后将它复制3份,分别放在3个文件夹下面

可以通过 查找,获取到刚才安装的Redis的配置文件,恢复至原始状态,然后将其复制到创建的3个目录下,

find / -name redis.conf

在这里插入图片描述

可以使用管道组合命令,一键拷贝

echo 7001 7002 7003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/redis.conf

在这里插入图片描述

修改数据保存目录为3个文件夹的各自目录以及端口号,分别执行一下命令,可快捷完成更改

sed -i -e 's/6379/7001/g' \
       -e 's/dir .\//dir \/tmp\/7001\//g' \
       -e 's/logfile ""/logfile "\/tmp\/7001\/redis.log"/g' \
       7001/redis.conf;
sed -i -e 's/6379/7002/g' \
       -e 's/dir .\//dir \/tmp\/7002\//g' \
       -e 's/logfile ""/logfile "\/tmp\/7002\/redis.log"/g' \
       7002/redis.conf;
sed -i -e 's/6379/7003/g' \
       -e 's/dir .\//dir \/tmp\/7003\//g' \
       -e 's/logfile ""/logfile "\/tmp\/7003\/redis.log"/g' \
       7003/redis.conf;

声明每个实例的IP,因为虚拟机本身具有多个IP,为避免混乱和变化,最好为每个实例指定固定的IP地址,可使用一下命令快捷修改

sed -i '1a replica-announce-ip 192.168.119.101' 7001/redis.conf;
sed -i '1a replica-announce-ip 192.168.119.101' 7002/redis.conf;
sed -i '1a replica-announce-ip 192.168.119.101' 7003/redis.conf;

更改redis.conf的binddaemonize属性,允许远程访问和后台运行,可以进行快捷修改

sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7001/redis.conf;
sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7002/redis.conf;
sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7003/redis.conf;

然后分别运行3个redis实例,运行的时候需指定特定的redis.conf文件

redis-server 7001/redis.conf;
redis-server 7002/redis.conf;
redis-server 7003/redis.conf;

运行完成之后,查看redis进程

ps -ef |grep redis

在这里插入图片描述

可以看到3个redis实例已经全部运行成功。

最后一步,配置主从关系,我们以 7001 实例作为master,7002,7003作为replica/slave,然后分别修改redis.conf配置文件

其中,master主节点不需要修改,只需要修改两个从节点即可

# 格式: replicaof host port
replicaof 192.168.119.101 7001

# 或者
salveof 192.168.119.101 7001

Redis5.0之前使用 slaveof 参数,Redis5.0之后使用 replicaof 参数,两者效果完全一样

使用快捷命令进行修改

sed -i '1a replicaof 192.168.119.101 7001' 7002/redis.conf;
sed -i '1a replicaof 192.168.119.101 7001' 7003/redis.conf;

修改完成之后,关闭3个redis进程,然后全部重启,进行测试即可。

kill 9 57781 57787 57811

至此,Redis集群搭建完成

2.Docker 搭建主从架构

准备3份redis的配置文件(使用默认配置即可),修改配置文件中RDB的存储位置为/data/。且将配置文件分别存储在不同的目录下:

关键配置信息如下:

# 1.修改 dir 的值为 /data/
dir /data/
# 2.注释掉 bind 127.0.0.1,允许远程访问
#bind 127.0.0.1
# 3.关闭保护模式,修改参数值为 no
protected-mode no

运行以下 docker 命令,创建3个redis容器:

docker run --name redis-master \
            -v /usr/local/redis-1:/usr/local/etc/redis \
            -v /usr/local/redis-1/data/:/data/ \
            -p 7001:6379 \
            -p 27001:27001 \
            --network bridge \
            -d redis:latest \
            redis-server /usr/local/etc/redis/redis.conf;

docker run --name redis-replica-1 \
            -v /usr/local/redis-2:/usr/local/etc/redis \
            -v /usr/local/redis-2/data/:/data/ \
            -p 7002:6379 \
            -p 27002:27002 \
            --network bridge  \
            -d redis:latest \
            redis-server /usr/local/etc/redis/redis.conf;

docker run --name redis-replica-2 \
            -v /usr/local/redis-3:/usr/local/etc/redis \
            -v /usr/local/redis-3/data/:/data/ \
            -p 7003:6379 \
            -p 27003:27003 \
            --network bridge \
            -d redis:latest \
            redis-server /usr/local/etc/redis/redis.conf;
  • -v /usr/local/redis-1/data/:/data/ :将redis容器的RDB文件目录/data/挂载到Linux服务器目录/usr/local/redis-1/data/

创建容器时容器的网络模式使用了桥接模式(bridge),这是Docker中的默认网络模式,创建了一个虚拟网卡docker0,通过docker0网桥以及iptables nat表配置与宿主机通信,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。

使用以下命令查看虚拟网卡docker0的配置信息

iptables -t nat -nvL

在这里插入图片描述

使用虚拟网卡docker0提供的Network Namespace以及ip地址,可以实现容器之间的相互通信。关于Docker网络模式内容,请查阅Dokcer官网

我们以7001端口的容器作为主机,由上图可知,映射端口为7001端口的容器的虚拟网桥地址是172.12.0.2:6379

在从机(replace / slave) 的配置文件 redis.conf 中指定主机(master)的IP地址和端口号

# 格式: replicaof host port
replicaof 172.17.0.2 6379

# 或者
salveof 172.17.0.2 6379

Redis5.0之前使用 slaveof 参数,Redis5.0之后使用 replicaof 参数
在这里插入图片描述
完成之后重启容器进行测试,此时主机的日志会给出提示,如下图:
在这里插入图片描述
主从架构搭建完成以后,只有主机(master)可以做写操作,而从机(slave)只能做读操作

3.主从数据同步原理

1.全量同步

主从第一次同步是全量同步
在这里插入图片描述
master如何判断slave是不是第一次来同步数据?这里会用到两个很重要的概念:

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

因此slave做数据同步,必须向master声明自己的Replication Idoffsetmaster才能判断到底需要同步哪些数据。

Redis底层通过Replication Id来判断是否为第一次数据,如果masterReplication Idslave不一样,则说明是第一次同步数据。

全量同步的完整流程:

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

2.增量同步

主从第一次同步是全量同步,但如果slave重启后同步,则执行增量同步

在这里插入图片描述

repl_baklog有大小上限,写满后会覆盖最早的数据。如果slave断开的时间过长,导致数据被覆盖,则无法实现增量同步,只能再次进行全量同步。

3.主从同步优化

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

Redis的主-从-从集群结构:

在这里插入图片描述

4.小结

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

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

什么情景下进行全量同步?

  • slave第一次连接master节点时
  • slave断开连接太久,repl_baklog中的数据已经被覆盖时

什么时候做增量同步?

  • slave节点断开之后重新连接,并且在repl_baklog中能找到offset

四 Redis哨兵

1.哨兵的作用和原理

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

在这里插入图片描述

1.Sentinel的作用如下:

  • 服务状态监控:不间断的检查Redis的master和slave是否按照预期工作
  • 故障恢复:如果master故障,则Sentinel会将一个slave提升为master,当故障恢复后也以新的master为主(实现主从的切换)
  • 通知:Sentinel充当Redis客户端的服务发现开源,当集群发生故障转移时,会将最新消息推送给Redis客户端

2.服务状态监控的过程:

Sentinel基于心跳机制检测服务状态,每隔1s向集群中的每个示例发送ping命令

  • 主观下线:如果Sentinel节点发现某个实例未在规定时间内响应,则认为该示例主观下线
  • 客观下线:若超过指定数量(quorum)的Sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例的一半

3.选举新的master:

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

4.故障转移

在这里插入图片描述

2.搭建Redis哨兵集群

创建一个sentinel.conf,或者在Redis中的解压文件中找到sentinel.conf,并且将它分别复制到3个redis实例的目录下7001 7002 7003

可以使用一下快捷复制命令,注意,要在7001 7002 7003的父级目录下使用一下命令

echo 7001 7002 7003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/sentinel.conf

然后修改相关配置参数

# 指定哨兵的端口号
port 27001
# 指定当前哨兵节点的IP地址
sentinel announce-ip 192.168.119.101
# 指定主节点哨兵的实例名称(这里是mymaster),及主节点IP,和主观下线值(quorum),超过这个值则重新选举主节点
sentinel monitor mymaster 192.168.119.101 7001 2
# 主节点的最大断开时长
sentinel down-after-milliseconds mymaster 5000
# 故障恢复时间
sentinel failover-timeout mymaster 60000
# 是否后台运行
daemonize yes
# 指定工作路径,即sentinel.conf的目录
dir "/tmp/7001"
# 指定日志文件目录
logfile ""

一键修改全部实例的命令

sed -i -e 's/26379/27001/g' \
       -e 's/dir \/tmp/dir "\/tmp\/7001" /g' \
       -e 's/daemonize no/daemonize yes/g' \
       -e 's/logfile ""/logfile "\/tmp\/7001\/sentinel.log"/g' \
       -e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
       7001/sentinel.conf;
sed -i -e 's/26379/27002/g' \
       -e 's/dir \/tmp/dir "\/tmp\/7002" /g' \
       -e 's/daemonize no/daemonize yes/g' \
       -e 's/logfile ""/logfile "\/tmp\/7002\/sentinel.log"/g' \
       -e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
       7002/sentinel.conf;
sed -i -e 's/26379/27003/g' \
       -e 's/dir \/tmp/dir "\/tmp\/7003" /g' \
       -e 's/daemonize no/daemonize yes/g' \
       -e 's/logfile ""/logfile "\/tmp\/7003\/sentinel.log"/g' \
       -e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
       7003/sentinel.conf;

一键添加哨兵配置

sed -i '1a sentinel announce-ip 192.168.119.101' \
     7001/sentinel.conf;
sed -i '1a sentinel announce-ip 192.168.119.102' \
     7002/sentinel.conf;
sed -i '1a sentinel announce-ip 192.168.119.103' \
     7003/sentinel.conf;

一键运行哨兵

redis-sentinel 7001/sentinel.conf;
redis-sentinel 7002/sentinel.conf;
redis-sentinel 7003/sentinel.conf;

通过 ps -ef|grep redis 查看,可以看到3个sentinel实例,和3个redis实例均已运行

4.RedisTemplate哨兵模式

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

1.引入RedisTemplate

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.配置Redis的读写分离

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

此处的ReadFrom是一个枚举,用来配置Redis的读取策略,包括下面选择:

  • MASTER:从主节点读取
  • MASTER_PREFERRED:优先读取主节点,主节点不可用时才读取从节点
  • REPLICA:从replica节点读取
  • REPLICA_PREFERRED:优先从replica节点读取,从节点不可用时从主节点读取

3.修改配置文件

spring:
  redis:
    sentinel:
      # 配置主节点名称
      master: mymaster
      # 配置redis-sentinel集群节点
      nodes:
        - 192.168.119.101:27001
        - 192.168.119.101:27002
        - 192.168.119.101:27003

五 Redis分片集群

1.分片集群结构

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

  • 海量数据存储问题
  • 高并发写入的问题

可以考虑使用分片集群来解决上述问题,分片集群的特征:

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

在这里插入图片描述

2.分片集群搭建

分片集群数量较多,以最小的分片集群作为示例演示,包含3个master,其中每一个master节点包含一个slave节点

在这里插入图片描述

2.集群搭建准备,之前搭建集群和哨兵的目录可以删除掉

# 1.创建文件夹
mkdir 7001 7002 7003 8001 8002 8003
# 2.拷贝配置文件
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/redis.conf

3.关键参数配置

port 7001
#开启集群配置
cluster-enabled yes
#节点配置文件,无需创建,由Redis自行维护,放开即可
cluster-config-file nodes-6379.conf
#节点心跳失败的超时时间
cluster-node-timeout 15000
#持久化文件存放目录,可根据实际情况修改
dir /data/
#绑定地址,允许远程访问
bind 0.0.0.0
#后台运行
daemonize yes
#注册的实例ip及端口号,根据实际情况修改
replica-announce-ip 192.168.119.101
replica-announce-port 7001
#保护模式
protected-mode no
#数据库数量,一般情况下修改为1即可,默认情况下为16
databases 1
# 日志,配置日志文件的位置及名称
logfile /tmp/7001/cluster.log

4.运行Redis集群实例

redis-server 7001/redis.conf;
redis-server 7002/redis.conf;
redis-server 7003/redis.conf;
redis-server 8001/redis.conf;
redis-server 8002/redis.conf;
redis-server 8003/redis.conf;

在这里插入图片描述

5.此时的 Redis 实例已经全部运行,但是之间并无直接关联,现在还需要去创建集群。运行以下命令创建集群

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

运行之后,redis会给出一个集群方案

在这里插入图片描述

确定这个集群分案没有问题后,则输入 yes ,至此,分片集群创建完成。

6.检查集群状态,可以通过主节点的端口号查看集群状态,下面以 7001 作为示例

redis-cli -p 7001 cluster nodes

在这里插入图片描述

3.散列插槽

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

1.Redis中的数据 key 不与节点绑定

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

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

例如:key 值为 num, 那么就根据 num 值进行计算,如果是 { shawn } num,则根据 shawn 计算。计算方法为利用CRC16算法得到一个哈希值,然后对16383取余,最后得到的结果就是 slot 值

2.集群模式下进入Redis

进入指定端口的集群存测试数据存储

redis-cli -c -p [port]
# 示例
redis-cli -c -p 7001

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

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

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

  • 如果同一类数据计算出来的插槽是一样,则可以保证它们都存储在同一个Redis实例中
  • 只需要保证 key 的有效部分一样即可,例如 key 都以 {typeId}为前缀

4.集群伸缩

redis-cli --cluster 中提供了很多方法,可以通过一下命令查看

# 查看 redis-cli --cluster 命令帮助
redis-cli --cluster help
# 添加节点
redis-cli --cluster add-node [new_host]:[new_port] [exist_host]:[exist_port]

准备一个8004的文件夹,和 redis 配置文件,修改端口号等相关配置参数,然后运行该实例

#运行 8004 实例
redis-server 8004/redis.conf

1.添加为主节点

#1.不加任何选项时默认添加的为主节点(master),且不分配插槽
redis-cli --cluster add-node 192.168.119.101:8004 192.168.119.101:7001
#2.为指定节点分配插槽,使用 reshard 命令
redis-cli --cluster reshard 192.168.119.101:7001

在这里插入图片描述

输入要分配的插槽数量,这里我们输入 3000 ,然后敲击回车

在这里插入图片描述

输入要接收插槽的节点ID,我们通过命令查询到,输入即可

在这里插入图片描述

输入想要获取的插槽所在的节点Id,这里我们从 7002 中拿出3000个插槽分配给 8004,这里输入 7002 的节点Id,

在这里插入图片描述

从多个节点分配时,输入多个,结束时输入 done 即可,

等待分配完成,查看集群状态
在这里插入图片描述

此时,原本在 7002 节点上的 【6462 - 9461】插槽 被迁移到了 8004 节点,且上面的数据也被迁移了过来。

这也说明了,Redis分片集群中的数据,是和插槽绑定的,而非节点

2.添加为从节点

# 增加 --cluster-slave 选项,添加进去的 8004 节点将变成从节点,并且指定主节点的ID(通过redis-cli -p 7001 cluster nodes查询)
redis-cli --cluster add-node 192.168.119.101:8004 192.168.119.101:7001 \
          --cluster-slave \
          --cluster-master-id 94a1478400df69fcf0e197fb6a129a39298f359b

通过redis-cli -p 7001 cluster nodes查询,可以看到 7001 下面有两个从节点
在这里插入图片描述

更多命令通过 redis-cli --cluster help 查看用法。

3.删除指定节点

删除从节点时使用命令直接删除即可,删除主节点时需要把主节点上的插槽转移到其他节点上方可删除

# 语法示例
redis-cli --cluster del-node [host]:[port] 【nodeId】
# 删除示例
redis-cli --cluster del-node 192.168.119.101:8004 e12ee3d58e459db6272750dafef410ed5d632f8c

5.故障转移

尝试关闭或重启指定集群,然后观察日志

# 关闭指定集群,以 7002 为例
redis-cli -p 7002 shutdown
# 重启指定集群,以 7002 为例
redis-server 7002/redis.conf

会发现原来的从节点变成了主节点,而重启之后的 7002 实例,变成了 从节点,成功完成了主从切换,实现了故障转移

6.手动故障转移

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

在这里插入图片描述

手动的FialOver命令支持三种不同模式

  • 默认模式:流程如上图
  • force:省略了对 offset 的校验
  • takeover:直接执行第5步,忽略数据一致性,忽略 master 状态 和其他 master 意见

具体实现:

以 7002 示例为例,现在的 7002 已经是从节点,而它的主节点为 8001

在这里插入图片描述

连接 7002 ,输入 cluster fialover 命令,实现主从切换

# 1.连接 7002 实例
redis-cli -c -p 7002
# 2.输入命令 cluster fialover
cluster fialover
# 3.退出 exit
exit
# 4.查看集群状态
redis-cli -p 7001 cluster nodes

在这里插入图片描述

可以看到,此时的 7002 已经变成了 master 节点,而 8001 则变成了 slave 节点,从而实现手动故障转移

7.RestTemplate访问分片集群

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

1.引入redis的starter依赖

2.配置分片集群地址

spring:
  redis:
    cluster:
      # 指定分片集群中的每一个节点信息
      nodes:
        - 192.168.119.101:7001
        - 192.168.119.101:7002
        - 192.168.119.101:7003
        - 192.168.119.101:8001
        - 192.168.119.101:8002
        - 192.168.119.101:8003
        - 192.168.119.101:8004

3.配置读写分离

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# 调用Python

一、简介 IronPython 是一种在 NET 和 Mono 上实现的 Python 语言&#xff0c;由 Jim Hugunin&#xff08;同时也是 Jython 创造者&#xff09;所创造。 Python是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python是…

电子器件系列31:ULN2003 芯片详解

主体转自&#xff1a; uln2003驱动电路_身在江湖的郭大侠的博客-CSDN博客_uln2003 一、uln2003有什么作用 ULN2003是大电流驱动阵列&#xff0c;多用于单片机、智能仪表、PLC、数字量输出卡等控制电路中。可直接驱动继电器等负载。 输入5VTTL电平&#xff0c;输出可达500mA/…

167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < numbers…

每天一道大厂SQL题【Day07】教育领域SQL实战

每天一道大厂SQL题【Day07】教育领域SQL实战 大家好&#xff0c;我是Maynor。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深大数据选手&#xff0c;深知SQL重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于大数据岗面试中的经典SQL题&#…

C进阶实战通讯录

C语言实战通讯录C语言实战通讯录前言整理逻辑整体框架初始化通讯录添加联系人显示联系人删除联系人查找联系人修改联系人销毁通讯录保存联系人信息加载联系人信息所有源码&#xff1a;test.c&#xff1a;Contact.h:Contact.c:C语言实战通讯录 前言 这次用C语言实现通讯录是一…

Nginx_4

Nginx负载均衡 负载均衡概述 早期的网站流量和业务功能都比较简单&#xff0c;单台服务器足以满足基本的需求&#xff0c;但是随着互联网的发展&#xff0c;业务流量越来越大并且业务逻辑也跟着越来越复杂&#xff0c;单台服务器的性能及单点故障问题就凸显出来了&#xff0c…

VHDL语言基础-组合逻辑电路-基本逻辑门电路

数字电路中的四种基本操作是与、或、非及触发器操作&#xff0c;前三种为组合电路&#xff0c;后一种为时序电路。与非 、或非和异或的操作仍然是与、或、非的基本操作。与、或、非、与非、或非和异或等基本逻辑门电路为常用的门电路。 二输入与非门是一种常用的简单逻辑电路&a…

当下最流行的 ChatGPT :前世今生

GPT 不是凭空而出&#xff0c;它是经过了很多人的努力&#xff0c;以及很长一段时间的演化得来的。因此&#xff0c;梳理一下 GPT 的庞大 “家族” 还是很有必要的&#xff0c;看看他继承了什么&#xff0c;学习了什么&#xff0c;又改进了什么&#xff0c;这样也能更好地理解 …

C++设计模式(11)——桥接模式

亦称&#xff1a; Bridge 意图 桥接模式是一种结构型设计模式&#xff0c; 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#xff0c; 从而能在开发时分别使用。 问题 抽象&#xff1f; 实现&#xff1f; 听上去挺吓人&#xff1f; 让我们慢慢来&…

linux笔记 diff及patch的制作与使用

相关命令展示 为方便查阅博客使用&#xff0c;预先展示相关命令 diff命令 diff -uN old.txt new.txt > patch_test.patch单个文件&#xff0c;不需要使用-r参数 diff 选项参数 旧文件&#xff08;夹&#xff09; 新文件&#xff08;夹&#xff09; > 补丁diff命令的常…

PS快速入门系列

01-界面构成 1菜单栏 2工具箱 3工县属性栏 4悬浮面板 5画布 ctr1N新建对话框&#xff08;针对画布进行设置&#xff09; 打开对话框&#xff1a;ctrl0&#xff08;字母&#xff09; 画布三种显示方式切换&#xff1a;F 隐藏工具箱&#xff0c;工具属性栏&#xff0c;悬浮面板…

Linux perf probe 的使用(三)

文章目录前言一、Dynamic Tracing二、kprobes2.1 perf kprobe 的使用2.2 kprobe Arguments3.3 tcp_sendmsg()3.3.1 Kernel: tcp_sendmsg()3.3.2 Kernel: tcp_sendmsg() with size3.3.2 Kernel: tcp_sendmsg() line number and local variable三、uprobes的使用3.1 perf uprobe …

PTA L1-043 阅览室

前言&#xff1a;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时&#xff0c;管理员输入书号并按下S键&#xff0c;程序开始计时&#xf…

Java NIO学习(一):Java NIO概述

一、 IO 概述IO 的操作方式通常分为几种&#xff1a;同步阻塞 BIO、同步非阻塞 NIO、异步非阻塞 AIO。&#xff08;1&#xff09;在 JDK1.4 之前&#xff0c;我们建立网络连接的时候采用的是 BIO 模式。&#xff08;2&#xff09;Java NIO&#xff08;New IO 或 Non Blocking I…

【C语言】速刷日记

这里总结10道C语言经典题型问题&#xff0c;有兴趣可以刷刷看。1.递归问题12.递归问题23.短路求值问题4.字符数组定义问题5.二维数组元素访问问题6.指针问题7.二维数组地址问题8.前置与后置问题9.求1的个数问题10.求1的个数移位操作符1.递归问题1 给定 fun 函数如下&#xff0…

Fluid-架构详细解析

前言Fluid 是云原生分布式数据集编排和加速引擎&#xff0c;主要服务于云原生场景下的数据密集型应用&#xff0c;例如大数据应用、AI 应用等。其目标是为AI与大数据云原生应用提供一层高效便捷的数据抽象&#xff0c;将数据从存储抽象出来&#xff0c;以便实现&#xff1a;通过…

服务调用分布式session

目录一、nginx动静分离二、服务调用1、创建配置zmall-cart购物车模块2、创建配置zmall-order订单模块3、服务调用三、spring session实战1、什么是Spring Session2、为什么要使用Spring Session3、错误案例展示4、配置spring-session四、二级域名问题五、用户登录一、nginx动静…

从某种程度上来看,产业互联网是一次对于互联网的弥补和修正

如果对当下我们正在经历的这样一个时代进行一次定义的话&#xff0c;我更加愿意将其划归到产业互联网的范畴里。可能有人会说&#xff0c;这与产业互联网并无联系&#xff0c;因为从本质上来看&#xff0c;当下我们所经历的这样一个时代&#xff0c;其实是与互联网并没有太多联…

(深度学习快速入门)第五章第一节2:GAN经典案例之MNIST手写数字生成

获取pdf&#xff1a;密码7281 文章目录一&#xff1a;数据集介绍二&#xff1a;GAN简介&#xff08;1&#xff09;简介&#xff08;2&#xff09;损失函数三&#xff1a;代码编写&#xff08;1&#xff09;参数及数据预处理&#xff08;2&#xff09;生成器与判别器模型&#x…

Acwing---112.雷达设备

雷达设备1.题目2.基本思想3.代码实现1.题目 假设海岸是一条无限长的直线&#xff0c;陆地位于海岸的一侧&#xff0c;海洋位于另外一侧。 每个小岛都位于海洋一侧的某个点上。 雷达装置均位于海岸线上&#xff0c;且雷达的监测范围为 d&#xff0c;当小岛与某雷达的距离不超…