常见Redis部署方式有以下几种:
- 单节点
- 主从
- Sentinel
- Cluster
- 代理(Twemproxy等)
下面只分析Sentinel、Cluster、代理的高可用
一、Sentinel
定时任务
- 每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。 判断是否客观下线。
- 每个Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。用来发现slave节点、确认主从关系。
- 每个 Sentinel 会以每两秒一次的频率, 通过发布与订阅功能, 向被它监视的所有主服务器和从服务器的 sentinel:hello 频道发送一条信息, 信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID (runid)。Sentinel 之间也通过该频道互相交换信息。
主观下线和客观下线
Redis 的 Sentinel 中关于下线(down)有两个不同的概念:
- 主观下线指的是单个 Sentinel 实例对服务器做出的下线判断。
- 客观下线指的是多个 Sentinel 实例在对同一个服务器做出SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。最后当达成这一共识的sentinel个数达到前面说的quorum设置的这个值时,就会对该master节点下线进行故障转移。quorum的值一般设置为sentinel个数的二分之一加1,例如3个sentinel就设置2
故障转移
领导者选举
选举出一个sentinel节点去完成故障转移
- 每个做主观下线的sentinel节点向其他sentinel节点发送上面那条命令,要求将它设置为领导者。
- 收到命令的sentinel节点如果还没有同意过其他的sentinel发送的命令(还未投过票),那么就会同意,否则拒绝。
- 如果该sentinel节点发现自己的票数已经过半且达到了quorum的值,就会成为领导者
- 如果这个过程出现多个sentinel成为领导者,则会等待一段时间重新选举。
master转移
- 选择slave-priority(slave节点优先级配置)最高的slave节点,(默认都是一样的)例如:如果我们有两台slave在两台机器上,一台配置较高,我们希望当master挂掉优先选配置高的,就可以配置该值为slave中最高的。如果存在最高则返回,不存在继续
- 选择复制偏移量最大的节点(复制得最完整,与master节点的数据一致性更高),如果存在则返回,不存在继续
- 如果以上两个条件都不满足,选runId最小的(启动最早的)。
转移完成之后
sentinel通知客户端变更节点信息,客户端连接新master。如果故障master重新上线,则成为slave节点。
二、Cluster
Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接,节点间使用 gossip 协议来传播集群的信息。其redis-cluster架构图如下:
哈希槽算法
HASH_SLOT = CRC16(key) mod 16384
键空间被分割为 16384 槽(slot),集群的最大节点数量是 16384 个(然而建议最大节点数量设置在1000这个数量级上)。所有的主节点都负责 16384 个哈希槽中的一部分。
假设有三个节点,则每个节点槽区间为:
节点A覆盖0-5460;
节点B覆盖5461-10922;
节点C覆盖10923-16383.
gossip 协议
gossip 协议广播,集群节点通过PING/PONG消息实现节点通信,传播节点槽信息。
每个节点都保存其他节点的信息:
- 节点的 IP 地址和 TCP 端口号。
- 各种标识。
- 节点使用的哈希槽。
- 最近一次用集群连接发送 ping 包的时间。
- 最近一次在回复中收到一个 pong 包的时间。
- 最近一次标识节点失效的时间。
- 该节点的从节点个数。
- 如果该节点是从节点,会有主节点ID信息。
Redis Cluster主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。
其三主三从架构如下所示:
集群部署
搭建集群工作分为三步:
- 准备节点
- 节点握手 (MEET消息)
- 分配槽(addslots)
手动MEET、addslots部署参考:https://blog.csdn.net/men_wen/article/details/72871618
Redis官方redis-trib.rb工具部署参考:https://www.cnblogs.com/wuxl360/p/5920330.html
集群扩容与收缩
参考:https://blog.csdn.net/men_wen/article/details/72896682
增加或减少主节点时,集群会将迁移部分槽。其余主节点更新节点使用的哈希槽情况。
MOVED 重定向
客户端连接集群中某个主节点,如果刚好这个节点就是对应这个哈希槽,那么这个查询就直接被节点处理掉。否则这个节点会查看它内部的 哈希槽 -> 节点ID 映射,然后给客户端返回一个 MOVED 错误。
ASK 重定向
ASK与MOVED类似,只不过ASK重定向发生在虚拟槽的迁移过程中。
故障检测
Redis的gossip协议还可以通过PING/PONG消息实现传播主从状态、节点故障信息等。因此故障检测也是就是通过消息传播机制实现的。
PFAIL 标识:
当一个节点在超过 NODE_TIMEOUT 时间后仍无法访问某个节点,那么它会用 PFAIL 来标识这个不可达的节点。无论节点类型是什么,主节点和从节点都能标识其他的节点为 PFAIL。
FAIL 标识:
当下面的条件满足的时候,会使用这个机制来让 PFAIL 状态升级为 FAIL 状态:
- 某个节点,我们称为节点 A,标记另一个节点 B 为 PFAIL。
- 节点 A 通过 gossip 字段收集到集群中大部分主节点标识的节点 B 的状态信息。
- 如果集群中过半数的主节点都认为节点 B 标记为 PFAIL 状态,或者在 NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT 这个时间内是处于 PFAIL 状态。
如果以上所有条件都满足了,那么节点 A 会:
- 标记节点 B 为 FAIL。
- 向所有可达节点发送一个FAIL 消息。
FAIL 消息会强制每个接收到这消息的节点把节点 B 标记为 FAIL 状态。
故障转移
当一个节点从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤:
- 复制下线主节点的所有从节点里面,会有一个从节点被选中(选举过程下文会给出介绍)。
- 被选中的从节点会执行SLAVEOF no one命令,成为新的主节点。
- 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。
- 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
- 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。
纪元(epoch):
-
配置纪元(config epoch):
configEpoch是类似于一个版本号的东西,是关于cluster slot的一个版本号。一般来说,每一个master节点的configEpoch都是不同的(假如存在相同的configEpoch,会有冲突处理算法来保证每个节点的configEpoch不同),并且每一个节点的configEpoch不会轻易发生改变(节点改变的情况见下面)。由于configEpoch是单调递增的,因此当某一个节点的configEpoch改变之后,其它节点就会更新对该节点所声称拥有的slots的认识。 -
当前纪元(current epoch):
configEpoch只是关于slots分布的一个版本号,而currentEpoch才是真正的逻辑时钟。通过gossip协议,集群中的currentEpoch总是一致的(当然,特殊情况除外,如网络分区发生等)。currentEpoch越高,代表节点的配置或者操作越新。
config epoch的变化:
- 在集群初始化的时候,通过读取配置文件nodes.conf中的configEpoch值,对每一个节点的configEpoch进行赋值
- 集群重置(resetCluster)时,configEpoch被置为0
- 当一个槽迁移到某一个节点之后,尤其是集群发生resharding的时候;当发生故障转移(failover force)时,节点的configEpoch会被置为currentEpoch + 1
- 若两个节点的configEpoch相同,并且当前节点的nodename更大,则当前节点的configEpoch=currentEpoch+1(configEpoch发生冲突时的处理)
- 当master发生fail的之后,slave获得大多数master的投票时,设置slave的configEpoch为failover_auth_epoch
- cluster set-config-epoch命令强制设置configEpoch
- 当接受到其它节点发来的gossip消息的时候,设置configEpoch为消息中得configEpoch
选举过程
从节点通过广播一个 FAILOVER_AUTH_REQUEST 数据包给集群里的每个主节点来请求选票。然后从节点最大等待NODE_TIMEOUT*2的时间来回复(但总是至少2秒)。
一旦一个主节点给这个从节点投票,会回复一个 FAILOVER_AUTH_ACK,并且在 NODE_TIMEOUT * 2 这段时间内不能再给同个主节点的其他从节点投票。这不是保证安全性所必需的,但对于防止多个从节点在大约同一时间被选中(即使使用不同的configEpoch)非常有用,这通常是不需要的。
从节点会忽视所有带有的时期(epoch)参数比 currentEpoch 小的回应(ACKs),这样能避免把之前的投票的算为当前的合理投票。
一旦从节点接收到大于等于N/2+1个主节点的ACK,它就赢得了选举。否则,如果在两次NODE_TIMEOUT期间未达到多数(但总是至少2秒),则选举将中止,并且在NODE_TIMEOUT * 4之后将再次尝试选举(并且始终至少4秒)。
三、Twemproxy
待补充
《Redis设计与实现》
https://redis.io/topics/sentinel
https://redis.io/topics/cluster-tutorial
https://redis.io/topics/cluster-spec
https://blog.csdn.net/qq2430/article/details/80716313
https://my.oschina.net/u/3371837/blog/1790026
https://blog.csdn.net/fengshizty/article/details/51368004/
https://blog.csdn.net/men_wen/article/category/6769467/1
https://blog.csdn.net/qq1137623160/article/details/79184686
https://www.cnblogs.com/wuxl360/p/5920330.html
https://www.jianshu.com/p/92183354183e