Redis分布式缓存

文章目录

  • 一、 概述
    • 1. 单节点Redis存在的问题
    • 2. 单节点Redis问题针对解决方案
  • 二、Redis持久化
    • 1. RDB持久化
    • 2.RDB异步持久化原理介绍
    • 3. AOF持久化
    • 4. ROB和AOF对比
  • 三、Redis主从架构
    • 1. 搭建主从架构
    • 2. 主从数据同步原理
  • 四、Redis哨兵
    • 1. 哨兵的作用和原理
    • 2.搭建哨兵集群
    • 3. RedisTemplate的哨兵模式
  • 五、Redis分片集群
    • 1. 搭建分片集群
    • 2. 散列插槽
    • 3. 集群伸缩
    • 4. 故障转移
    • 5. RedisTemplate访问分片集群

一、 概述

1. 单节点Redis存在的问题

  • 数据丢失问题:Redis是基于内存存储的,服务进行重启后容易发生数据丢失问题
  • 并发能力问题:单节点Redis在实际业务中很难处理高并发的场景
  • 故障恢复问题:Redis一旦出现故障对整个服务的影响是致命的,所以要保证Redis是时刻可用的
  • 存储能力问题:内存的有限容量觉得了单节点redis的存储能力是极其有限的,单节点redis很难满足海量数据存储

2. 单节点Redis问题针对解决方案

  • 针对数据丢失问题—实现Redis数据的持久化
  • 针对并发能力问题—搭建Redis集群,实现读写业务的分离
  • 针对存储能力问题—搭建分片集群(类似于ElasticSearch),利用插槽机制实现动态扩容
  • 针对故障恢复问题—利用Redis的哨兵机制,实现对Redis实时健康检测和自动恢复
    下面针对这四个方面逐一介绍

二、Redis持久化

1. RDB持久化

RDB(Redi Database file-redis数据备份文件),也叫做Redis数据快照,利用快照技术,将内存快照记录到磁盘中。当Redis出现故障后,可以从磁盘中的快照快速恢复到出现故障前的状态。(快照文件就是RDB文件,默认保存在Redis当前运行目录)
Redis使用save命令,保存当前内存快照,注意save命令是Redis主进程来实现的,而Redis本身又是单线程的,所以在执行Sava命令时,其它所有的命令都会进入阻塞状态,所以这种方式消耗是非常大的,所以推荐使用bgsave命令,bgsave命令痛殴开辟另外的子线程来执行RDB,而不会影响到Redis主进程,所以性能会更高。最后Redis在关闭前会自动执行一次RDB
测试

  • 启动Redis服务
redis-server 

Redis分布式缓存_第1张图片

  • 退出redis服务

会发现redis自动做了一个RDB
Redis分布式缓存_第2张图片

同时也可以在当前运行文件中也可以查看到redis的快照
Redis分布式缓存_第3张图片
总结:我们可以看出Redis在退出服务会自动做一次RDB,而我们的需求时在Redis运行过程中(毕竟实际业务中redis一般不会退出)做数据持久化(以预防服务器出现宕机等情况),实际上Redis内部有触发RDB的机制,可以在redis.conf中进行配置,格式如下:

save 900 1 #900s内,如果至少有一个key被修改,则执行bgsave,如果是sava “” 则表示禁用RDB
save 300 1 #300s内,如果至少有一个key被修改,则执行bgsave,如果是sava “” 则表示禁用RDB
save 60 10000 #60s内,如果至少有一万个key被修改,则执行bgsave,如果是sava “” 则表示禁用RDB

RDB其它配置也可以在Redis.conf文件中设置

rdbcompression yes #是否压缩,建议不开启,压缩会消耗cpu资源
dbfilename dump.rdb #RDB文件名
dir ./ #文件保存的路径目录

测试

  1. 按照配置文件方式启动redis
redis-server /usr/local/redis-6.2.5/redis.conf
  1. 连接redic-cli加入一条新的数据

Redis分布式缓存_第4张图片

2.RDB异步持久化原理介绍

前面我们知道bgsave是主线程fork出来的子进程来执行数据持久化,下面我细化其持久化过程:

  1. 主进程fork一个bgsave进程(这部分主进程会阻塞但时间很短)
  2. 子进程共享主进程的内存资源(包括页表等资源),完成fork后读取内存数据并写入RDB文件
  3. 完成一步异步持久化过程

Redis分布式缓存_第5张图片

问题:由于fork了一个bgsave进程,现在就会引入多线程中的问题,思考如果子线程在读数据的同时主线程又在写数据,这种情况下就可能出现脏读的问题,而redis采用的解决方案是copy-on-write技术:

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

总结:RDB缺点

  • 数据安全漏洞:RDB执行间隔长(我们通过save 60 1可以设置,设置太短RDB次数太多,cpu性能下降),两次间隔之间主线程向Redis中写入数据存在数据丢失的风险
  • forks子进程、压缩(可关闭)、写RDB文件到磁盘都比较耗时

3. AOF持久化

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

Redis分布式缓存_第6张图片
AOF数据持久化的原理就是,在Redis接收到的每一条命令都会有序的保存到一个AOF文件中,一旦服务器宕机数据丢失,我们只需要重新执行之前的命令就可以达到恢复数据的效果。在Redis中,AOF是默认关闭的,需要修改redis.conf配置文件来开启AOF

appendonly yes #是否开启AOF功能,默认是no
appendfilename "appendonly.aof #AOF文件名

AOF的命令记录的频率也可以通过redis.conf来配置

appendfsync always #表示每执行一条写命令,立即记录AOF文件(同步刷盘-可靠性高,几乎不丢失数据-性能差)
appendfsync eveysec #写命令执行完放入AOF缓冲区,然后表示每隔一秒将缓冲区数据写入AOF文件,是默认方案(每秒刷盘-性能适中-最多丢失1s数据)
appendfsync no #写命令执行完放入AOF缓冲区,由操作系统觉得何时将缓冲区内容写入磁盘(操作系统控制-性能最好-可靠性差,可能丢失大量数据)

测试

  1. 在配置文件中AOF进行配置,并重启redis
save ""   #禁用rdb
appendonly yes
  1. 向redis中插入数据
set jack chai

出现aof文件(里面记录了写命令)
在这里插入图片描述
3. 重启redis并发现可以看到刚刚插入的数据,说明数据持久化成功
在这里插入图片描述
问题:设想一下这种常见,我对redis中key为jack的数据又进行了多次set赋值操作,此时AOF会将所有set都记录下来,事实上我们知道,set只有最后一次是有意义的,这就导致了AOF记录了很多无效的指令,所以一般AOF会比RDB大的多。而redis是通过bgrewirteaof(它与主线程也是异步的)来解决这个问题的,该命令可以让AOF文件执行重写功能,用最少的命令达到相同效果。

在这里插入图片描述
测试
1.多次set jack值查看aof文件

Redis分布式缓存_第7张图片
2.使用bgrewirteaof重写aof文件,查看效果(BGREWRITEAOF对aof进行了特殊编码)

BGREWRITEAOF

在这里插入图片描述
问题:什么时候触发写AOF文件,前面在介绍RDB持久化方案时,我们知道RDB的执行时间可以时Redis退出时自动执行,或者在通过在配置文件中配置save命令来执行RDB操作。而AOF在Redis中会触发一个阈值区执行操作,阈值同样可以在redis.conf中配置:

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

4. ROB和AOF对比

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

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

三、Redis主从架构

1. 搭建主从架构

前面说到单节点的Redis并发能力是有限的,要进一步提高Redis的并发能力,需要搭建Redis主从集群,实现读写业务分离。
Redis分布式缓存_第8张图片
搭建Redis集群实战

  • 基本描述

集群共包括三个Redis节点,一个主节点,两个从节点(由于资源有限,我这里在一台centos7中搭建三个redis服务,分别开启三个不同的端口来模拟)

  • 创建目录
cd /tmp
mkdir 7011 7012 7013 #分别对应每个redis服务的端口号

在这里插入图片描述

  • 拷贝配置文件(redis.conf)到每个实例目录(上面创建的目录)

这里我这台centos 7中已经安装了一个redis服务,所以我直接拷贝其配置文件即可

cp redis.conf /tmp/7011
cp redis.conf /tmp/7012
cp redis.conf /tmp/7013
  • 修改每个实例的端口、工作目录

修改每个文件夹的配置文件,将端口修改为7001、7002、7003,将rdb文件保存位置都修改为自己所在的目录

sed -i -e 's/6379/7011/g' --e 's/dir .\//dir \/tmp\/7011\//g' 7011/redis.conf
sed -i -e 's/6379/7012/g' --e 's/dir .\//dir \/tmp\/7012\//g' 7012/redis.conf
sed -i -e 's/6379/7013/g' --e 's/dir .\//dir \/tmp\/7013\//g' 7013/redis.conf

其实上面的命令就是执行了文本中指定内容的替换

  • 修改每个实例的声明IP
    虚拟机本身有多个IP地址,未来避免将来发生混乱,需要在redis.conf中指定一个实例的绑定ip信息,格式如下:
replica-announce-ip 172.16.23.146 #这是我虚拟机的ip地址
sed -i '1a replica-announce-ip 172.16.23.146' 7011/redis.conf #1a表示在配置文件第一行后面追加一行
sed -i '1a replica-announce-ip 172.16.23.146' 7012/redis.conf
sed -i '1a replica-announce-ip 172.16.23.146' 7013/redis.conf
  • 启动三个redis服务
redis-server 7011/redis.conf
redis-server 7012/redis.conf
redis-server 7013/redis.conf
  • 查看端口运行情况
netstat -tnlp | grep :70

在这里插入图片描述

  • 开启redis主从关系

上面已经准备好了三个redis服务,但现在这三个服务没有任何关系,要配置主从可以使用replicaof或者slaveof(5.0以前)命令,而主从关系存在着两种模式:

  1. 修改配置文件永久生效: 在redis.conf中添加一行配置slaveof
  2. 使用redis-cli客户端连接到redis服务,执行slaveof命令slaveof (重启后生效)

(1) 模式二演示:

redis-cli -p 7012  -a 123321  #用密码的形式连接到7012端口
slaveof 172.16.23.146 7011  #添加从属关系

注意如果你的主节点设置了密码的话要给每个从节点授权密码,具体方法是在从节点的配置文件中添加如下命令(不然可能连接不上):

masterauth *****

在这里插入图片描述
其它一台机器同理(这里将7011作为了master主机)
查看集群状态

INFO replication

Redis分布式缓存_第9张图片
测试集群
我现在主节点中添加一条num :1 的数据,看从节点是否可以访问到

发现访问成功,说明主节点的数据已经成功拷贝到从节点上
(2)模式一只是将slaveof信息写入了配置文件中(从节点的),只需要向配置文件中添加下面的命令,然后重启从节点即可,这里就不操作了:

replicaof 127.0.0.1 7011
masterauth 123321

2. 主从数据同步原理

在上面已经完成了一个简单的redis集群了,也成功测试了从主节点添加信息并从节点可以读到信息了(注意在集群中只有主节点可以写入数据,从节点只能读数据,redis集群默认实现了读写分离业务),下面来分析一下主从数据同步的原理所在:

  • 主从第一次同步是全量同步
    Redis分布式缓存_第10张图片

思考:master如何知道slave是不是第一次来?
这里就要介绍两个重要概念:

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

因此slave做数据同步时,必须向master声明自己的Replication IDoffset,master才可以判断到底需要同步哪些数据
总结:全量同步流程

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

Redis分布式缓存_第11张图片
注意:repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间太久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。

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

  1. 在master中配置repl-diskless-sync yes启动无磁盘复制,避免全量同步时的磁盘IO(前面说到全量同步时是生产RDB文件后保留到磁盘,然后从磁盘读取发送给slave,而这条命令是直接将RDB文件通过网络的IO发送给slave而不保存到磁盘中,适合网络带宽很多的情况)
  2. Redis单节点上的内存占用不要太大(这样就会减少RDB传输的数据量),减少RDB导致的过多磁盘IO
  3. 以上是全量同步的优化方法,但在实际业务中还是要减少全量同步的次数,所以我们可以提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  4. 限制一个master上的slave节点数量(从节点多了,主节点做数据同步的压力是很大的),入够实战太多slave,则可以采用主-从-从的链式结构(分担部分节点给从节点做从节点,让部分从节点承当数据同步压力),减少master的压力

四、Redis哨兵

思考:slave节点宕机恢复后可以找master节点同步数据,那master节点宕机了怎么办?
解决方案:我们可以对集群状态实时进行监控,当master宕机后我们可以立即找一个从节点来作为新的主节点(主从替换操作),而这个监控任务就交到了Redis哨兵手里

1. 哨兵的作用和原理

Redis提供了哨兵(Sentinel)机制来实现主从几圈的自动故障恢复。哨兵的结构和作用如下:

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

Redis分布式缓存_第12张图片
问题:sentinel是如何监控服务状态的?
sentinel其实是基于心跳机制监控服务状态的,每隔1s向集群的每个实例发送ping命令(和nacos的原理类似):

  • 主观下线:如果某sentinel节点发现某节点未在规定时间响应,则认为该节点主观下线
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该节点主观下线,则该节点客观下线。quorum数量最好超过sentinel集群中的一半

问题:如果选举新的master?
一旦发现master故障,sentinel需要在slave中选择一个新的作为master节点,依据如下:

  • 首先会判断slave节点与master节点断开时间的长短,如果超过指定值(down-after-milliseconds*10—配置文件可以配置),则会排序该slave节点
  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永远不参与选举
  • 如果slave-priority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
  • 最后判断slave节点的运行id大小,越小优先级越高(这里相当于随便选一个)

问题:选中slave后如何做故障转移?
当选中一个slave后,故障转移的步骤如下:

  • sentinal给备选的slave发送slaveof no one命令,让该节点称为master
  • sentinal给其它slave发送slaveof (slave的ip地址和端口)命令,让这些slave成为新的master的从节点,开始新的master上同步数据
  • 最后sentinal会将故障节点标记为从节点,当故障节点重启后会成为新的master的从节点

2.搭建哨兵集群

说明:这里在之前搭建的集群的基础上搭建哨兵集群,这里我们搭建一个三个节点组成的sentinel集群,来监管Redis集群

  • 准备实例和配置
  1. 创建文件夹
cd /tmp
mkdir s1 s2 s3
  1. 在每个目录下创建一个sentinal.conf文件,添加如下内容(内容要调整):
touch sentinal.conf #创建文件
#################################
#sentinel monitor mymaster 127.0.0.1 7011 2:监控名为mymaster的主节点,主节点地址为127.0.0.1,端口号为7011,至少需要2个哨兵同意主节点不可用。
sentinel monitor mymaster 172.16.23.146 7011 2 
#主节点的密码(注意从节点的密码要和主节点保持一致)
sentinel auth-pass mymaster  123321
#sentinel down-after-milliseconds mymaster 5000:如果在5000毫秒内无法与主节点建立连接,Sentinel将标记该主节点为不可用。
sentinel down-after-milliseconds mymaster 5000
#sentinel failover-timeout mymaster 30000:故障切换的超时时间为30秒。
sentinel failover-timeout mymaster 30000
#在故障切换期间,每个从节点只能与新的主节点同步一次。
sentinel parallel-syncs mymaster 1
port 27001
#logfile "/var/log/redis/sentinel_27001.log":Sentinel实例的日志文件
logfile "/tmp/s1/sentinel.log"
dir "/tmp/s1"#Sentinel实例的工作目录。
  1. 启动集群
    打开三个控制台分别执行以下命令启动sentinel服务
redis-sentinel s1/sentinel.conf
redis-sentinel s2/sentinel.conf
redis-sentinel s3/sentinel.conf

  1. 测试效果:让主节点宕机查看sentinel日志文件
  • 连接7011控制台,关闭redis服务
 shutdown

在这里插入图片描述

  • 查看sentinel日志
#sdown表示主观下线
6531:X 31 Mar 2023 19:02:40.992 # +sdown master mymaster 127.0.0.1 7011
#当odown客观下线
6531:X 31 Mar 2023 19:02:41.070 # +odown master mymaster 127.0.0.1 7011 #quorum 2/2
6531:X 31 Mar 2023 19:02:41.070 # +new-epoch 1
6531:X 31 Mar 2023 19:02:41.070 # +try-failover master mymaster 127.0.0.1 7011
#vote-for-leader,选主节点
6531:X 31 Mar 2023 19:02:41.071 # +vote-for-leader bfb12a5712f4dc8bee97ac10ba963ba47fc70c78 1
6531:X 31 Mar 2023 19:02:41.073 # 20134ef7d28b05a8a0c4b8ac0dfef38b09d98bc3 voted for bfb12a5712f4dc8bee97ac10ba963ba47fc70c78 1
6531:X 31 Mar 2023 19:02:41.133 # +elected-leader master mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:41.133 # +failover-state-select-slave master mymaster 127.0.0.1 7011
#7012被选为主节点
6531:X 31 Mar 2023 19:02:41.234 # +selected-slave slave 172.16.23.146:7012 172.16.23.146 7012 @ mymaster 127.0.0.1 7011
#发送slaveof-noone命令给7012
6531:X 31 Mar 2023 19:02:41.235 * +failover-state-send-slaveof-noone slave 172.16.23.146:7012 172.16.23.146 7012 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:41.301 * +failover-state-wait-promotion slave 172.16.23.146:7012 172.16.23.146 7012 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:42.081 # +promoted-slave slave 172.16.23.146:7012 172.16.23.146 7012 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:42.081 # +failover-state-reconf-slaves master mymaster 127.0.0.1 7011
#广播消息
6531:X 31 Mar 2023 19:02:42.150 * +slave-reconf-sent slave 172.16.23.146:7013 172.16.23.146 7013 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:43.158 * +slave-reconf-inprog slave 172.16.23.146:7013 172.16.23.146 7013 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:43.159 * +slave-reconf-done slave 172.16.23.146:7013 172.16.23.146 7013 @ mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:43.213 # -odown master mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:43.214 # +failover-end master mymaster 127.0.0.1 7011
6531:X 31 Mar 2023 19:02:43.214 # +switch-master mymaster 127.0.0.1 7011 172.16.23.146 7012
6531:X 31 Mar 2023 19:02:43.214 * +slave slave 172.16.23.146:7013 172.16.23.146 7013 @ mymaster 172.16.23.146 7012
6531:X 31 Mar 2023 19:02:43.214 * +slave slave 127.0.0.1:7011 127.0.0.1 7011 @ mymaster 172.16.23.146 7012

从上面日志文件中可以看出sentinel的故障处理的过程,同时我们可以进入7012的控制台看现在的集群状况
Redis分布式缓存_第13张图片
现在恢复之前宕机的主节点,查看是否会变成从节点,下面进入7011后,执行set num2 1命令发现已经没有权限了
在这里插入图片描述

3. RedisTemplate的哨兵模式

在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化(前面已经演示)及时更新链接信息。Spring的RedisTemplate底层利用lettuce实现李节点的感知和自动切换。

Lettuce 是一个高级 Java Redis 客户端,它提供了基于 Java NIO(非阻塞 I/O)的连接和编码基础设施。在 Spring 应用程序中,Lettuce 通常被用作 Redis 的底层客户端库,与 Spring Data Redis 集成,以提供对 Redis 数据结构、缓存和其他功能的支持。

下面在idea中进行测试

  1. 新建一个SpringBoot项目(很简单这里就不演示了)
  2. 在pom文件中引入redis的starter的依赖
    <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
  1. 在application.yml中指定sentinel的相关信息
logging:
  level:
    io.lettuce.core: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
spring:
  redis:
    sentinel:
      master: mymaster   #指定主节点
      nodes: #指定redis-sentinal集群信息
        - 172.16.23.146:27001
        - 172.16.23.146:27002
        - 172.16.23.146:27003
    password: 123321 #redis密码
  1. 配置主从读写分离(主负责写,从负责读)
   @Bean
    public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomize(){
    //对lettuce的自定义配置
      return  clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
    }

这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择

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

我们现在就可以在日志中读取关于Redis集群的相关信息了

Redis分布式缓存_第14张图片

五、Redis分片集群

问题:前面介绍了主从以及哨兵可以解决高可用、高并发读写问题,但是依然有两个问题没有解决:

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

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

  1. 集群中有多个master节点,每个master保存不同的数据(所以数据存储的上限变为了master数量)
  2. 每个master都可以有多个slave节点
  3. master之间通过ping检测彼此健康状态(不需要哨兵了)
  4. 客户端请求可以访问集群任意节点,最终都会转发到正确节点
    Redis分布式缓存_第15张图片

1. 搭建分片集群

分片集群需要的节点数量比较多,这里我们搭建一个最小的分片集群,保护3个master节点,每个master节点保护一个slave节点。

  • 准备实例和配置
  1. 创建目录
mkdir 7001 7002 7003 8001 8002 8003
  1. 在tmp下创建一个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
replica-announce-ip 172.16.23.146
#保护模式
protected-mode no
#数据库数量
datebases 1
#日志
logfile /tmp/6379/run.log 
  1. 将文件拷贝到每个目录中
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf #管道

Redis分布式缓存_第16张图片
4. 修改每个文档下conf文件的内容

printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
  • 启动
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf
  • 查看运行结果
    Redis分布式缓存_第17张图片

关闭所有进程用这种方式:

printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown
  • 创建集群

这里我用的redis 6所有用的5.0之后版本的创建方式,5.0之前有别的创建方式

redis-cli --cluster create --cluster-replicas 1 172.16.23.146:7001 172.16.23.146:7002 172.16.23.146:7003 172.16.23.146:8001 172.16.23.146:8002 172.16.23.146:8003

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

  • 查看集群状态
redis-cli -p 7001 cluster nodes

在这里插入图片描述

集群搭建就完成了

2. 散列插槽

前面我们创建集群的过程中会看到散列插槽的分配情况如下:
Redis分布式缓存_第18张图片

redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,为什么要插槽

思考当我们需要向redis中插入数据的时候,数据该插到哪里,这其实是和插槽有关的(数据和插槽绑定,就不会因为节点的宕机而数据发生丢失了)!

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

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

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

  • 测试

(1) 连接一个master节点

 redis-cli -c -p 7001 #-c表示集群模式

(2) 加入数据
Redis分布式缓存_第19张图片
(3) 结论:redis分片集群操作数据的过程:

  1. 根据key利用hash算法计算key值,即插槽值
  2. 根据插槽值找到对应插槽的节点
  3. 重定向到相应的节点,对数据进行操作

思考如何将同一类数据固定保存到一个redis实例(节点)?

利用前面介绍到 的{}机制

3. 集群伸缩

集群的伸缩功能是指实现集群的动态增加或移除节点

add-node new_host:new_port existing_host:existing_port
         --cluster-slave #不指定默认添加是主节点
         --cluster-master-id <arg>

案例:向集群中添加一个新的master节点,并向其存储num=10

  • 启动一个细腻的redis,端口为7004
  • 添加7004到之前的集群,并作为一个master节点
  • 给7004节点分配插槽,使得num这个key可以存储到7004节点
  1. 创建一个新的节点(和前面的操作一样这里不赘叙了)
  2. 将7004加入到redis集群中的master节点
redis-cli --cluster add-node 172.16.23.146:7004 172.16.23.146:7001 


3. 给7004分配指定的插槽
Redis分布式缓存_第20张图片

redis-cli --cluster reshard 172.16.23.146:7001


查看插槽分配情况:

redis-cli -p 7001 cluster nodes

在这里插入图片描述
4. 插入数据
在这里插入图片描述

4. 故障转移

虽然分片集群不存在哨兵机制,但是也可以实现故障转移功能。当一个集群的master宕机后,同样会选择一个slave节点来充当主节点,然后宕机的节点恢复后就会成为新的主节点的slave节点,具体的流程如下:

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

上面上redis自动故障转移的过程
利用cluster failover命令可以实现手动让集群的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移,具体流程如下:
Redis分布式缓存_第21张图片
手动的Failover支持三种不同的模式:

  • 缺省:默认的流程,会执行上面的1-6步
  • force:省略流程中对offset的校验
  • takeover:直接执行第五步,忽略数据的一致性、忽略master状态和其它master的意见

测试:让8001成为master

  1. 利用redis-cli连接这个节点
redis-cli -c -p 8001
  1. 执行cluster failover命令
CLUSTER FAILOVER  #默认执行上面六个流程
  1. 查看结果
redis-cli -p 7001 cluster nodes

在这里插入图片描述

5. RedisTemplate访问分片集群

Redis Template底层同样基于lettuce实现了分片集群的支持,使用的步骤于哨兵模式基本一致,下面简单使用一下

  1. 引入Redis的starter依赖(和哨兵模式一致)
  2. 配置分片集群地址
spring:
  redis:
    cluster:
      nodes: #指定分片集群的每一个节点信息
        - 172.16.23.146:7001
        - 172.16.23.146:7002
        - 172.16.23.146:7003
        - 172.16.23.146:7004
        - 172.16.23.146:8001
        - 172.16.23.146:8002
        - 172.16.23.146:8003

  1. 配置读写分离(和哨兵模式一样)
  2. 查看结果

Redis分布式缓存_第22张图片

你可能感兴趣的:(Redis,redis,缓存,分布式)