文章目录
- 一、 概述
- 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服务
会发现redis自动做了一个RDB
同时也可以在当前运行文件中也可以查看到redis的快照
总结:我们可以看出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 ./ #文件保存的路径目录
测试
- 按照配置文件方式启动redis
redis-server /usr/local/redis-6.2.5/redis.conf
- 连接redic-cli加入一条新的数据
2.RDB异步持久化原理介绍
前面我们知道bgsave是主线程fork出来的子进程来执行数据持久化,下面我细化其持久化过程:
- 主进程fork一个bgsave进程(这部分主进程会阻塞但时间很短)
- 子进程共享主进程的内存资源(包括页表等资源),完成fork后读取内存数据并写入RDB文件
- 完成一步异步持久化过程
问题:由于fork了一个bgsave进程,现在就会引入多线程中的问题,思考如果子线程在读数据的同时主线程又在写数据,这种情况下就可能出现脏读的问题,而redis采用的解决方案是copy-on-write技术:
- 当主进程执行读操作时,访问共享内存
- 当主线程执行写操作时,则会拷贝一份数据执行写操作
总结:RDB缺点
- 数据安全漏洞:RDB执行间隔长(我们通过save 60 1可以设置,设置太短RDB次数太多,cpu性能下降),两次间隔之间主线程向Redis中写入数据存在数据丢失的风险
- forks子进程、压缩(可关闭)、写RDB文件到磁盘都比较耗时
3. AOF持久化
AOF(Append Only File-追加文件):Redis处理的每一个命令都会记录在AOF文件,可以看作是命令日志文件
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缓冲区,由操作系统觉得何时将缓冲区内容写入磁盘(操作系统控制-性能最好-可靠性差,可能丢失大量数据)
测试
- 在配置文件中AOF进行配置,并重启redis
save "" #禁用rdb
appendonly yes
- 向redis中插入数据
set jack chai
出现aof文件(里面记录了写命令)
3. 重启redis并发现可以看到刚刚插入的数据,说明数据持久化成功
问题:设想一下这种常见,我对redis中key为jack的数据又进行了多次set赋值操作,此时AOF会将所有set都记录下来,事实上我们知道,set只有最后一次是有意义的,这就导致了AOF记录了很多无效的指令,所以一般AOF会比RDB大的多。而redis是通过bgrewirteaof
(它与主线程也是异步的)来解决这个问题的,该命令可以让AOF文件执行重写功能,用最少的命令达到相同效果。
测试
1.多次set jack值查看aof文件
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集群实战
- 基本描述
集群共包括三个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以前)命令,而主从关系存在着两种模式:
- 修改配置文件永久生效: 在redis.conf中添加一行配置
slaveof<masterip> <masterport>
- 使用redis-cli客户端连接到redis服务,执行slaveof命令
slaveof <masterip><masterport>
(重启后生效)
(1) 模式二演示:
redis-cli -p 7012 -a 123321 #用密码的形式连接到7012端口
slaveof 172.16.23.146 7011 #添加从属关系
注意如果你的主节点设置了密码的话要给每个从节点授权密码,具体方法是在从节点的配置文件中添加如下命令(不然可能连接不上):
masterauth *****
其它一台机器同理(这里将7011作为了master主机)
查看集群状态
INFO replication
测试集群
我现在主节点中添加一条num :1
的数据,看从节点是否可以访问到
发现访问成功,说明主节点的数据已经成功拷贝到从节点上
(2)模式一只是将slaveof信息写入了配置文件中(从节点的),只需要向配置文件中添加下面的命令,然后重启从节点即可,这里就不操作了:
replicaof 127.0.0.1 7011
masterauth 123321
2. 主从数据同步原理
在上面已经完成了一个简单的redis集群了,也成功测试了从主节点添加信息并从节点可以读到信息了(注意在集群中只有主节点可以写入数据,从节点只能读数据,redis集群默认实现了读写分离业务),下面来分析一下主从数据同步的原理所在:
- 主从第一次同步是全量同步
思考:master如何知道slave是不是第一次来?
这里就要介绍两个重要概念:
Replication ID
:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replidoffset
:偏移量,随着记录在real_backlog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新
因此slave做数据同步时,必须向master声明自己的Replication ID
和offset
,master才可以判断到底需要同步哪些数据
总结:全量同步流程
- slave节点请求增量同步
- maste节点判断replid,发现不一致,拒绝增量同步
- master节点将完整内存数据生成RDB,发送RDB到 slave
- slave清空本地数据,加载master的RDB
- master将RDB期间的命令记录在repl_backlog中,并持续将log中的命令发送给slave
- slave执行接收到的命令,保存于master之间的同步
- 如果slave重启后同步,则执行增量同步
注意:repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间太久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。
可以从以下几个方面来优化redis主从集群:
- 在master中配置repl-diskless-sync yes启动无磁盘复制,避免全量同步时的磁盘IO(前面说到全量同步时是生产RDB文件后保留到磁盘,然后从磁盘读取发送给slave,而这条命令是直接将RDB文件通过网络的IO发送给slave而不保存到磁盘中,适合网络带宽很多的情况)
- Redis单节点上的内存占用不要太大(这样就会减少RDB传输的数据量),减少RDB导致的过多磁盘IO
- 以上是全量同步的优化方法,但在实际业务中还是要减少全量同步的次数,所以我们可以提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量(从节点多了,主节点做数据同步的压力是很大的),入够实战太多slave,则可以采用主-从-从的链式结构(分担部分节点给从节点做从节点,让部分从节点承当数据同步压力),减少master的压力
四、Redis哨兵
思考:slave节点宕机恢复后可以找master节点同步数据,那master节点宕机了怎么办?
解决方案:我们可以对集群状态实时进行监控,当master宕机后我们可以立即找一个从节点来作为新的主节点(主从替换操作),而这个监控任务就交到了Redis哨兵手里
1. 哨兵的作用和原理
Redis提供了哨兵(Sentinel)机制来实现主从几圈的自动故障恢复。哨兵的结构和作用如下:
- 监控:Sentinel会不断检查你的master和slave是否按照预期工作
- 自动故障恢复:如果master故障,sentinel会将一个slave提升为master。当故障节点恢复后也以新的master为主
- 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给redis客户端
问题: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集群
- 准备实例和配置
- 创建文件夹
cd /tmp
mkdir s1 s2 s3
- 在每个目录下创建一个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实例的工作目录。
- 启动集群
打开三个控制台分别执行以下命令启动sentinel服务
redis-sentinel s1/sentinel.conf
redis-sentinel s2/sentinel.conf
redis-sentinel s3/sentinel.conf
- 测试效果:让主节点宕机查看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的控制台看现在的集群状况
现在恢复之前宕机的主节点,查看是否会变成从节点,下面进入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中进行测试
- 新建一个SpringBoot项目(很简单这里就不演示了)
- 在pom文件中引入redis的starter的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 在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密码
- 配置主从读写分离(主负责写,从负责读)
@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分片集群
问题:前面介绍了主从以及哨兵可以解决高可用、高并发读写问题,但是依然有两个问题没有解决:
- 海量数据存储问题
- 高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
- 集群中有多个master节点,每个master保存不同的数据(所以数据存储的上限变为了master数量)
- 每个master都可以有多个slave节点
- master之间通过ping检测彼此健康状态(不需要哨兵了)
- 客户端请求可以访问集群任意节点,最终都会转发到正确节点
1. 搭建分片集群
分片集群需要的节点数量比较多,这里我们搭建一个最小的分片集群,保护3个master节点,每个master节点保护一个slave节点。
- 准备实例和配置
- 创建目录
mkdir 7001 7002 7003 8001 8002 8003
- 在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
- 将文件拷贝到每个目录中
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf #管道
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
- 查看运行结果
关闭所有进程用这种方式:
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会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,为什么要插槽?
思考当我们需要向redis中插入数据的时候,数据该插到哪里,这其实是和插槽有关的(数据和插槽绑定,就不会因为节点的宕机而数据发生丢失了)!
数据的key不是和节点绑定的,而是和插槽绑定的,redis会根据key的有效部分计算插槽值,分两种情况:
- key中包含"{}“中至少一个字符,”{}"中的部分是有效部分
- key中不包含"{}",整个key都是有效部分
eg:key是num,那么根据num计算,如果是{jakie}num,则根据jakie计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值
- 测试
(1) 连接一个master节点
redis-cli -c -p 7001 #-c表示集群模式
(2) 加入数据
(3) 结论:redis分片集群操作数据的过程:
- 根据key利用hash算法计算key值,即插槽值
- 根据插槽值找到对应插槽的节点
- 重定向到相应的节点,对数据进行操作
思考:如何将同一类数据固定保存到一个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节点
- 创建一个新的节点(和前面的操作一样这里不赘叙了)
- 将7004加入到redis集群中的master节点
redis-cli --cluster add-node 172.16.23.146:7004 172.16.23.146:7001
3. 给7004分配指定的插槽
redis-cli --cluster reshard 172.16.23.146:7001
查看插槽分配情况:
redis-cli -p 7001 cluster nodes
4. 插入数据
4. 故障转移
虽然分片集群不存在哨兵机制,但是也可以实现故障转移功能。当一个集群的master宕机后,同样会选择一个slave节点来充当主节点,然后宕机的节点恢复后就会成为新的主节点的slave节点,具体的流程如下:
- 首先是该节点于其它节点失去连接
- 然后是疑似宕机
- 最后确定下线,自动提升一个slave为新的master节点
上面上redis自动故障转移的过程
利用cluster failover命令可以实现手动让集群的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移,具体流程如下:
手动的Failover支持三种不同的模式:
- 缺省:默认的流程,会执行上面的1-6步
- force:省略流程中对offset的校验
- takeover:直接执行第五步,忽略数据的一致性、忽略master状态和其它master的意见
测试:让8001成为master
- 利用redis-cli连接这个节点
redis-cli -c -p 8001
- 执行cluster failover命令
CLUSTER FAILOVER #默认执行上面六个流程
- 查看结果
redis-cli -p 7001 cluster nodes
5. RedisTemplate访问分片集群
Redis Template底层同样基于lettuce实现了分片集群的支持,使用的步骤于哨兵模式基本一致,下面简单使用一下
- 引入Redis的starter依赖(和哨兵模式一致)
- 配置分片集群地址
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
- 配置读写分离(和哨兵模式一样)
- 查看结果