文章目录
- Redis多机数据库
- 一、主从复制
- 1、旧版复制功能的实现
- a、同步
- b、命令传播
- 2、旧版复制功能的缺陷
- 3、新版复制功能的实现
- a、部分同步功能
- b、复制实现步骤
- 4、心跳检测
- 二、哨兵
- 1、Sentinel概念
- 2、Sentinel初始化流程
- 3、故障转移过程
- 三、集群
- 1、几个概念
- 2、集群创建流程
- a、启动节点
- b、槽指派
- 3、集群执行命令流程
- moved错误
- slots_to_keys
- 4、重分片流程
- ask错误
- 5、伸缩流程
- 6、故障转移流程
- 7、消息
Redis多机数据库
Redis数据结构与对象:https://blog.csdn.net/qq_41822345/article/details/130456081
Redis单机数据库:https://blog.csdn.net/qq_41822345/article/details/130909789
先总结一下Redis的四种部署模式(除了第一种是单机数据库,其它都是多机数据库):
1、单机模式
概念:Redis单机模式是最简单的部署模式,Redis将数据存储在单个节点上。
优点
- 简单易用。架构简单,部署方便。
- 低延迟高性能。毕竟是单机,少了很多的网络传输。
缺点
- 无法保证数据的可靠性。
- 处理能力有限。受限于单核CPU的处理能力。
- 内存容量有限 。因此一般不会用于生产环境。
2、主从复制模式
概念:主从模式是指可以让一个服务器(slave)去复制另一个服务器(master)的数据。
优点
- 数据备份。Master能自动将数据同步到Slave,且同步是以非阻塞bgsave的方式进行的。
- 读写分离。 分担Master的读压力。
缺点
- 不具备自动容错与恢复功能。
- 有数据不一致的风险。比如master宕机。
- 难以支持在线扩容,Redis的容量仍然受限于单机配置
3、哨兵模式
概念: 哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。
优点
- 主从复制模式有的优点,哨兵模式也有。
- 系统可用性更高。master挂掉可以自动进行切换。
缺点
- 主从复制模式有的优点,哨兵模式也有。
- 需要额外的资源来启动sentinel进程,实现相对复杂一点。
4、集群模式
概念:集群模式实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。
优点
- 无中心架构。
- 可维护性。降低运维成本,提高系统的扩展性和可用性。
- 高可扩展。可线性扩展到1000多个节点,节点可动态添加或删除。
- 高可用性。部分节点不可用时,集群仍可用。
缺点
- 架构复杂性。客户端实现的复杂性。开发难度提高。
- 数据通过异步复制,不保证数据的强一致性。
- slave节点只能作为数据备份,不能缓解读压力。
- 不支持多数据库,只有1个数据库空间db0。
- 运维复杂,需要面临更多问题:比如Key事务操作、big-key/hot-key的处理更加复杂。
一、主从复制
主从模式是指通过执行 slaveof 命令或设置 slaveof 选项,让一个服务器(slave)去复制另一个服务器(master)的数据。
只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。
主从复制是哨兵和集群能够实施的基础,因此可以说主从复制是Redis高可用的基石。
1、旧版复制功能的实现
Redis的复制功能分为 同步 和 命令传播 两个操作。
a、同步
一个redis实例变成另一个redis实例的从节点有三种方式。
- 启动前:在从服务器的配置文件中加入slaveof配置;
- 启动时:在redis-server启动命令后加入–slaveof;
- 启动后:直接在客户端执行命令:slaveof ,则该Redis实例成为从节点。
当redis实例通过上述方式变成一个从节点后,首先要执行同步操作——通过向主服务器发送SYNC命令来完成,步骤如下:
- step1:从服务节点向主服务器发送
sync
命令; - step2:主服务节点执行
bgsave
命令,后台生成RDB文件的同时,并使用缓冲区记录之后的所有写命令; - step3:主服务节点将生成的RDB文件发送给从服务器;
- step4:从服务节点载入RDB文件,将自己的数据库状态更新至主服务节点执行
bgsave
命令时的数据库状态; - step5:主服务器将缓冲区的所有写命令发送给从服务器;
- step6:从服务器执行这些写命令,将自己与主服务节点完全同步。
b、命令传播
当主服务器执行新的写命令时,主从服务器就可能出现不一致状态。这时主服务器需要对从服务器执行命令传播操作:将写命令传播给从服务器,这也是从服务器唯一能接收写命令的方式。
2、旧版复制功能的缺陷
以上复制方式在从节点初次复制主节点时,没有什么问题,但是当断线后重复制时,从节点需要再次发送sync
命令,相当于初次复制。这种为了让从服务器补足一小部分缺失的数据,但却要把所有数据重新复制一遍的做法是非常低效的。
SYNC
命令是一个非常耗费资源的操作【非必要时不要执行这个命令】
- 主服务器需要执行 bgsave 命令,这需要耗费主服务器大量的CPU、内存和I/O资源。
- 主服务器将RDB文件发送给从服务器,这占用主服务器和从服务器大量的网络资源。
- 从服务器在载入RDB文件的期间,会因为阻塞无法处理其它命令请求。
3、新版复制功能的实现
从Redis2.8版本开始,使用
PSYNC
代替SYNC
命令,它有两种模式:完整同步模式和部分同步模式。完整同步模式用于处理复制,等同于旧版复制功能。
部分同步模式解决了旧版复制功能在处理断线后重复制时出现的低效问题。
部分同步模式只需要将从服务器缺少的写命令发送给从服务器执行就可以了。
a、部分同步功能
部分同步功能的实现基于以下三个部分构成:
-
复制偏移量offset
-
复制积压缓冲区
-
服务器的运行ID
实现原理简述:从服务器发生断连时,它会向主服务器发送
PSYNC 主服务器运行ID 复制偏移量offset
请求进行部分同步。主服务器接收到从服务器的PSYNC
命令之后,首先对比从服务器传来的 主服务器运行ID ,如果和自己一致,那就检测从复制偏移量offset之后的数据是否存在于复制积压缓冲区,如果存在,则响应+CONTINUE
回复给从服务器,表示可以进行部分同步操作。
b、复制实现步骤
-
1、连接建立阶段【主从服务器状态属性中互相保存IP:port的过程】
-
step1、保存主节点信息
从服务器节点执行完slave of命令之后,会将主服务器的IP:port保存到从服务器节点状态中的masterhost属性和masterport属性,然后返回OK给客户端。
-
step2、建立socket连接
从服务器根据step1中保存的IP:port,创建连向主服务器的socket。主服务器在accept从服务器的连接之后,也会创建相应的socket。至此,主从服务器将基于此socket对进行通信。
-
step3、发送ping命令
从服务器向主服务器发出ping进行连接测试,如果收到响应pong说明连接测试成功。否则断开连接并重连master。
-
step4、身份验证
如果需要认证【主从双方都配置了密码】,则进行认证成功之后才能进行下一步。
-
step5、发送从节点端口信息
从服务器执行命令
replconf litsten-port port
向主服务器发送端口。主服务状态中属性中保存从服务器的IP:port。
-
-
2、数据同步阶段
- step1、首先,从节点根据当前状态,决定如何调用
psync
命令。- 全量复制:用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作。
- 部分复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。
- step2、主节点根据收到的
psync
命令,及当前服务器状态,决定执行全量复制还是部分复制。- 如果主节点版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送
sync
命令执行全量复制; - 如果主节点版本够新,且runid与从节点发送的runid相同,且从节点发送的offset之后的数据在复制积压缓冲区中都存在,则回复
+CONTINUE
,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可; - 如果主节点版本够新,但是runid与从节点发送的runid不同,或从节点发送的offset之后的数据已不在复制积压缓冲区中(在队列中被挤出了),则回复
+FULLRESYNC
,表示要进行全量复制,其中runid表示主节点当前的runid,offset表示主节点当前的offset,从节点保存这两个值,以备使用。
- 如果主节点版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送
- step1、首先,从节点根据当前状态,决定如何调用
-
3、命令传播阶段
- step1、在这个阶段主节点将自己执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。
- step2、在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:
PING
和REPLCONF ACK
。
4、心跳检测
主服务器通过向从服务器传播命令来更新从服务器的状态,保持主从服务器一致,同时从服务器也需要通过向主服务器发送 replconf ack
命令(每秒一次)来进行心跳检测。
心跳检测有三个作用:
-
1、检测主从服务器的网络连接状态
主服务器如果有超过1秒的时间没有收到来自从服务器的心跳检测命令
replconf ack
,那么主服务器就知道主从服务器之间的连接有问题了。通过info replication
命令可以看到从服务器最后一次向主服务器的心跳检测过了多少秒。一般延迟(lag)值在0到1秒之间属于正常。 -
2、辅助实现min-slaves配置选项
主服务器一般会配有设置
min-slaves-to-write
和min-slaves-max-lag
参数。表示如果slave少于min-slaves-to-write
个 或者有min-slaves-to-write
个slave的延迟(lag)都不小于min-slaves-max-lag
秒,则主服务器拒绝写命令。 -
3、检测命令丢失
从服务器会在发送
replconf ack
命令中告诉主服务器自己的复制偏移量offset,主服务器如果发现偏移量比自己少,就知道有命令发生的丢失,这时主服务器会从自己的复制积压区找到从服务器缺少的数据,重新发送给从服务器。(补发缺失数据 与 部分重同步原理一样【都是Redis 2.8版本新增的功能】,它们的区别是前者未发生断连,只是丢失了某些命令,而后者属于发生了断线并重连)
二、哨兵
哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。
由一个或多个Sentinel去监听(并且Sentinel也可以互相监视)任意多个主服务以及主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线的主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已经下线的主服务器继续处理命令请求。
1、Sentinel概念
-
Sentinel 本质上只是一个运行在特殊模式下的Redis服务器。
-
Sentinel可以监控任意多个Master和该Master下的Slaves( 即多个主从模式) 。
-
在同一个Sentinel哨兵下的,不同主从模型彼此之间相互独立,
-
Sentinel的三个任务
-
监控
Sentinel 会不断地检查主服务器和从服务器是否运作正常。
-
提醒
当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
-
自动故障迁移
当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作,将其中一个从服务器升级为新的主服务器。
-
-
Sentinel网络
- sentinel本身是监督者的身份,没有存储功能。
- 监控同一个Master的Sentinel会自动连接,组成一个分布式的Sentinel网络,互相通信并交换彼此关于被监视服务器的信息。
- 在整个体系中一个sentinel者或一群sentinels与主从服务架构体系是监督与被监督的关系。
- 为什么需要一个这样的Sentinel网络?
- sentinel在整个架构体系中有如下三种交互:sentinel与主服务器、sentinel与从服务器、sentinel与其他sentinel。
- 既然是交互,首先无可避免的就要构建这样的一个交互网络,需要节点的注册与发现、节点之间的通信连接、节点保活、节点之间的通信协议等。
2、Sentinel初始化流程
Redis在哨兵模式下,在监控主从服务器之间,需要先完成Sentinel节点的初始化。流程如下:
-
step1、启动并初始化Sentinel。
执行
redis-sentinel /path/to/your/sentinel.conf
命令 或者redis-server /path/to/your/sentinel.conf --sentinel
命令-
a、使用Sentinel专用代码。
Sentinel模式下Redis服务器只有以下功能:
- 复制命令,比如slaveof
- 发布与订阅功能,比如publish和subscribe
- 文件事件处理器,比如负责发送命令请求、处理命令回复
- 时间事件处理器,负责执行serverCron函数
-
b、初始化Sentinel状态,即初始化sentinalState结构
-
c、初始化Sentinel状态中的Master属性
-
d、创建连向主服务器的网络连接
-
-
step2、获取主服务器信息(每10秒一次)
-
step3、获取从服务器信息(每10秒一次)
-
step4、向主/从服务器发送信息(每2秒一次)
-
step5、接收来自主服务器和从服务器的频道信息
-
sentinel与主从服务器之间需要创建两种连接,一个是命令连接,一个是订阅连接。
-
sentinel需要通过命令连接向主从服务器发送信息,同时也需要通过订阅连接从主从服务器那接收信息。
-
通过交换信息,来进行下面的步骤。
-
-
step6、更新Sentinel字典
-
step7、创建连向其它Sentinel的命令连接
- 当一个Sentinel通过订阅连接从主服务器那获取到其它Sentinel时,它不仅会更新Sentinel字典中的实例结构,还会和新Sentinel之间互相创建新的命令连接,最终监视同一个主服务器的多个Sentinel形成了相互连接的Sentinel网络。
Sentinel模式下的服务器状态如图:
3、故障转移过程
Sentinel模式下,Sentinel初始化完成之后,在Sentinel网络中对所有的Matser和Slave进行监控。如果这时出现主服务器故障,则走如下流程。
-
step1、判定主观下线(每1秒一次)
Sentinel会向所有与它创建了命令连接实例(包括主服务器、从服务器、其它Sentinel)发送
Ping
命令。并根据有效回复Pong
、-Loading
、-Matserdown
(其它回复均为无效回复)进行检测。如果超过down-after-milliseconds
毫秒都没有有效回复,Sentinel就会把matser标记为主观下线。down-after-milliseconds
也是Sentinel判断master属下的slave,以及监视该matser的其它Sentinel是否进入主观下线的标准。 -
step2、判定客观下线
当Sentinel检测到主观下线后,就会询问其它监视该master的Sentinel,当超过一定数量的Sentinel都认为该Master已经下线后,就会将主服务器判定为客观下线。
- a、Sentinel发送
is-matser-down-by-addr
命令,对其它Sentinel进行询问 - b、其它Sentinel接收
is-matser-down-by-addr
命令,分析并检查master是否下线,并做出回复。 - c、Sentinel接收回复,对回复进行统计。当超过一定数量的Sentinel都认为该Master已经下线后,就会将主服务器判定为客观下线。
- a、Sentinel发送
-
step3、选举Sentinel Leader【基于Raft协议】
当检测出客观下线后,所有监视这个下线master的Sentinel需要先进行leader选举,由领导Sentinel进行故障转移操作。
- a、任何Sentinel都有成为leader的机会。每进行一次选择,Sentinel的epoch就会自增一次。
- b、每个发现master客观下线的Sentinel都会要求其它Sentinel将投自己一票,每个Sentinel都有一票,规则是先到先得。
- c、源Sentinel接收其它目标Sentinel的回复,并判断回复中epoch和leader-runid,如果epoch相同,并且leader-runid就是自己,那么说明目标Sentinel认可自己作为局部leader。
- d、当某个Sentinel被超过半数以上的Sentinel认可,那么这个Sentinel就会从局部leader成为全局leader,即领头Sentinel。
- e、因为每个Sentinel只有一票,且需要超过半数的投票,所有每个配置纪元epoch中最多只会有一个Sentinel被选举为leader。
- f、如果在给定的时间内,没有选举出leader,那么epoch+1,重新进行一次选举。
-
step4、选举新的主服务器。这是由领头Sentinel会在所有Slave中选出新的Master,选举规则如下:
- a、删除列表中所有处于下线或者短线状态的Slave。
- b、删除列表中所有最近5s内没有回复过领头Sentinel的INFO命令的Slave。
- c、删除所有与下线Master连接断开超过
down-after-milliseconds * 10
毫秒的Slave。 - d、领头Sentinel将根据Slave优先级,对列表中剩余的Slave进行排序,并选出其中优先级最高的Slave。
- e、如果有多个具有相同优先级的Slave,那么领头Sentinel将按照Slave复制偏移量,选出其中偏移量最大的Slave。
- f、如果有多个优先级最高,偏移量最大的Slave,那么根据运行ID最小原则选出新的Master。
-
step5、让其余所有Slave服务器复制新的Master服务器。
-
step6、当下线的Master重新上线后,将它变成新的Master服务器的Slave。
三、集群
Redis集群是由Redis提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。
- RedisCluster 是 Redis 的亲儿子,它是 Redis 作者自己提供的 Redis 集群化方案。
- redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster模式为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。
- Redis Cluster是一种服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。
相关参考:
Redis Cluster数据分片实现原理、及请求路由实现:https://blog.csdn.net/Seky_fei/article/details/107611850
Redis集群 - 图解 - 秒懂(史上最全):https://www.cnblogs.com/crazymakercircle/p/14698576.html#autoid-h3-7-0-0
- Redis 集群方案主要有3类:
- 1、基于官方的 Redis cluster 的服务端分片方案。(分片和路由都是在服务端实现)
- 2、使用类 codis 的代理模式架构,按组划分,实例之间互相独立。(分片和路由在代理实现)
- 3、代理模式和服务端分片相结合的模式。
1、几个概念
-
集群cluster、节点node、槽slot、键key之间的关系
-
cluster:node = 1:n
-
node:slot = 1:n
-
slot:key = 1:n
-
-
什么是槽slot?
Redis Cluster是Redis3.0引入的一种无中心化的集群,客户端可以向任何一个节点通信,Redis Cluster将数据的key通过将CRC16算法的结果取模16383后,分给16384个slot槽,集群的每个节点负责一部分hash槽,节点只负责管理映射到这个槽的KV数据,对于不是当前槽的KV数据,会向客户端发送一个MOVED,表示需要客户端重新重定向到其它节点。
-
为什么引入槽slot?
- 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
- 节点自身维护槽的映射关系,不需要客户端 或 代理服务维护数据分片关系。
- Redis Cluster的节点之间会共享消息,每个节点都知道另外节点负责管理的槽范围。每个节点只能对自己负责的槽进行维护 和 读写操作。
-
为什么没有使用一致性hash算法,而是使用了哈希槽预分片?
- 一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
- 当服务节点太少或者有节点挂掉时,容易造成数据倾斜——大量的缓存数据集中到了一台或者几台服务节点上。
-
为什么是16384个槽位(2^14)?
- 如果槽位是16384个,发送心跳信息的消息头是16384/8/1024 = 2k。
- Redis的集群主节点数量一般不会超过1000个。
- 集群中节点越多,心跳包的消息体内的数据就越多,如果节点过多,也会造成网络拥堵。
- 对于节点数在1000个以内的Redis Cluster,16384个槽位完全够用。
2、集群创建流程
集群创建流程就是一个创建集群实例数据结构体(一定都会有的结构体:redisServer、redisClient;集群模式下才有数据:clusterNode、clutserLink、clutserState等)的过程。
a、启动节点
-
Redis服务器在启动时会根据
cluster-enabled
配置选项是否为yes来决定是否开启服务器的集群模式。 -
Redis节点在集群模式下会继续使用所有在单机模式中使用的服务器组件:
- 文件事件处理器:处理命令请求和返回命令回复
- 时间事件处理器:执行
serverCron
函数(这时该函数也执行在集群模式下需要执行的操作:发送Gossip消息、下线检测、故障转移等) - redisDb数据库:保存键值对。不过集群模式下只有db0数据库。
- redisServer/redisClient数据结构:保存服务器状态和客户端状态。
- 持久化:RDB和AOF持久化。
-
初始化集群模式下数据结构:
- clusterNode:每个节点都会使用一个clusterNode来记录自己的节点状态——比如节点名称、ip和端口、slot信息等。
- clutserLink:保存连接节点所需的有关信息——比如socket套接字、输入缓冲区/输出缓冲区等。
- clutserState:每个节点都会使用一个clutserState来记录当前节点在集群中的状态——比如配置纪元、集群节点名单、槽指派信息等。
-
cluster meet命令
该命令用于将一个节点加入到集群中。这个过程类似三次握手,先由节点A向节点B发送meet消息;节点B返回pong消息给节点A,这是对meet消息的确认;最后节点A还会发送一次ping消息给节点B,告诉节点B收到了B的pong消息,至此握手完成。
-
Gossip协议
通过Gossip协议传播新节点的加入,让其它节点也与新节点进行握手。最终所有节点都会认识新节点。
b、槽指派
-
Redis集群的整个数据库被分为16384个槽slot,每个slot都必须分配到某个节点上,否则集群将处于下线状态。
-
节点的clusterNode结构记录了节点的槽指派信息。
- slots属性记录了节点负责处理槽信息。numslots属性记录了当前节点负责处理的槽数量
- slots是一个二进制位数组。长度为16384/8=2048,每个slots[i]有8位二进制,代表了16384个槽。
-
传播节点的槽指派信息
每个节点都会将自己的slots数组通过消息发送给集群中的其它节点。这样集群中的每个节点都会知道所有的槽指派信息。
-
记录集群的槽指派信息
集群的槽指派信息当然是记录在clutserState.slots中,它的每个数据项都是一个指向clusterNode结构的指针。
- clutserNode.slots数组只记录了当前节点的槽指派信息。
- clutserState.slots数组记录了全部节点的槽指派信息。
-
cluster addslots命令
通过该命令将槽指派给执行该命令的节点。
3、集群执行命令流程
当客户端向节点发送与数据库键key有关的命令时,需要经过以下步骤:
-
step1:计算键属于哪个槽
CRC16(key)& 16383
-
step2:判断槽是否由当前节点负责处理
根据step1计算出一个值 i 之后,判断clutserState.slots[i] = clutserState.myself
-
step3:如果clutserState.slots[i] != clutserState.myself。则根据clutserState.slots[i]所指向的clutserNode结构,获取IP:port,通过moved错误返回给客户端,从而转向负责处理槽slots[i]的节点。
moved错误
当节点发现键 key 所在的槽 slot[i] 并非由自己负责处理的时候,节点就会返回给客户端一个moved错误(包含了正确的负责处理槽 slot[i] 的节点),从而指引客户端专项正在负责槽的节点。
客户端会先根据moved错误提供的IP地址和端口来连接节点,然后再进行转向。
slots_to_keys
clutserState.slots_to_keys是一个跳跃表,它保存了槽与键之间的关系。通过记录各个数据库键所属的槽,节点可以很方便的对属于某个或某些槽的所有数据库键进行批量操作。比如重新分片。
4、重分片流程
Redis集群的重新分片操作是由管理组件redis-trib
负责执行的,它通过向源节点和目标节点发送命令来进行重新分片操作步骤以及执行的命令如下:
-
step1:开始对槽slot进行重新分片操作
-
step2:通知目标节点准备导入槽slot的键值对;
cluster setslot slot_id importing source_id
-
step3:通知源节点准备迁移槽slot的键值对;
cluster setslot slot_id migrating target_id
-
step4:向源节点获取最多count个属于槽slot的键值对;
cluster getkeyinslot slot_id count
-
step5:将step4获取的每个键迁移到目标节点(通过pipeline 机制批量迁移);
migrate target_id target_port kets
-
step6:重复执行step4和step5,直到属于槽slot的所有keys都被迁移完毕。
-
step7:传播槽指派信息;任意发送
cluster setslot slot_id node target_id
命令给某个节点,最终会通过Gossip协议传播给整个集群。
如果重新分片涉及到多个槽slot,那么redis-trib
对于每个槽分别执行上面的操作。
ask错误
moved错误和ask错误的区别?
区别在于槽slot[i]目前由哪个节点负责。
- 对于moved错误,说明slot[i]由其它节点负责,之后对于slot[i]的操作会直接发送到负责它的节点。
- 对于ask错误,说明slot[i]正在进行重分片,slot[i]的负责节点还在迁移中。
当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息;客户端再向正确的节点发送命令时,如果此时正在进行集群扩展或者缩空操作,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制。
5、伸缩流程
Redis集群中的每个node(节点)负责分摊这16384个slot中的一部分,也就是说,每个slot都对应一个node负责处理。当动态添加或减少node节点时,只需要将16384个槽做个再分配,将槽中的键值和对应的数据迁移到对应的节点上。
redis cluster提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容,也可以下线部分节点进行缩容。可以说,槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。
集群的伸缩流程原理基于重新分片原理。
-
a、集群扩容
当一个 Redis 新节点运行并加入现有集群后,我们需要为其迁移槽和槽对应的数据。首先要为新节点指定槽的迁移计划,会确保迁移后每个节点负责相似数量的槽,从而保证这些节点的数据均匀。
-
b、集群收缩
- 首先需要确认下线节点是否有负责的槽,如果有,需要把槽和对应的数据迁移到其它节点,保证节点下线后整个集群槽节点映射的完整性。
- 当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记改节点后可以正常关闭。
6、故障转移流程
Redis集群的节点分为Master和Slave,其中Master负责处理槽slot,而从节点slave用于复制master(一个节点成为从节点,并开始复制某个主节点的这一信息会通过消息发送给集群中的其它节点,最终集群中的所有节点都会知道这一信息),并可以在master下线时,代替下线主节点成为新主节点继续处理命令请求。
-
step1、故障检测
- 集群中的每个节点都会定期的向集群中的其它节点发送ping信息,来检测对方是否在线。
- 集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息——比如某个节点是否处于疑似下线状态(PFail)、还是已下线状态(Fail)。
- 集群中超过半数的matser节点都认为某个master节点为疑似下线状态(PFail),那么该master节点将被标记为已下线状态(Fail)。
-
step2、故障转移
- 1、当一个从节点发现自己的主节点为已下线状态(Fail),从节点会要求集群中的其它主节点选举新的主节点。(同样也是基于Raft协议。并且与选举领头Sentinel的流程非常相似。)
- 2、被选中的从节点执行
slave of no one
成为新的主节点。 - 3、新的主节点会主动撤销所有对已下线主节点的槽指派,并将槽指派给自己。
- 4、新的主节点向集群传播自己接管了下线的主节点。
- 5、新的主节点开始接收属于自己负责的槽的key命令。故障转移完成。
7、消息
Redis集群中的各个节点通过发送和接收消息来进行通信。节点发送的消息主要有以下五种:
- 1、meet消息:节点加入集群的命令。
- 2、ping消息:用于检测节点是否在线。集群中的每个节点每隔一秒就会从已知节点的列表中随机选择5个节点进行ping检测。如果当前节点发现自己记录的某些节点回复pong消息的时间超过了当前节点设置的cluster-node-timeout选线设置时长的一半,则当前节点主动发送ping检测,防止自己长时间都没有随机选择到某些节点进行ping检测,造成信息更新滞后。
- 3、pong消息:用于响应meet消息或者ping消息。也可用于向集群广播pong消息以刷新其它节点对自己的认识。
- Redis集群中各个节点通过
Gossip
协议来交换自己知道的节点状态消息。Gossip协议的实现由meet、ping、pong三种消息实现,它们都由clusterMsgDataGossip
结构组成。- 每次发送meet、ping、pong消息时,发送者都会从自己的已知节点列表中随机选出两个节点的信息(包括节点名称、ip和端口等)保存到要发送的消息结构体clusterMsgDataGossip结构里面。
- 4、fail消息:用于告知集群中的其它节点有节点进入fail状态。
在集群的节点数量比较大的情况下,单纯的使用
Gossip
协议来传播节点的已下线信息会给节点的信息更新带来一定的延迟,因为这个协议通常需要一段时间才能传播至整个集群,而发送Fail消息会让集群里的所有节点立即知道某个主节点已经下线,从而尽快判断是否需要将集群标记为下线,又或者对下线主节点进行故障转移。
- 5、publish消息:当节点接收到某个publish命令时,不仅会执行这个命令,并向集群广播这条publish命令。