redis cluster学习笔记
shard
Redis Cluster 特性之一是引入了槽的概念。一个 redis 集群包含 16384 个哈希槽,集群中的每个 redis 节点,分配到一部分槽。而集群使用公式 CRC16(key) % 16384 来计算每次请求的键 key 属于哪个槽,通过查询集群配置,便可知道 key 对应的槽属于哪个 redis 节点,然后再将请求打到该节点。举个例子,一个集群可以有两个节点,其中:
1. 节点 A 负责处理 0 号至 5000 号哈希槽。
2. 节点 B 负责处理 5001 号至 10000 号哈希槽。
3. 节点 C 负责处理 10001 号至 16383 号哈希槽。
通过上述公式,可对 key X 计算出一个值,该值为 0-16383 中的一个数。假设 key X 通过上述公式计算出来的值为 34,根据上面例子,34 即为槽标识,亦 key X 属于槽 34,而槽 34 分配到了节点 A,也就是说节点 A 负责 key X 的读写。
通过将哈希槽分布到不同节点,我们可以很容易地向集群中添加或者删除节点。比如说:
1 如果用户将新节点 D 添加到集群中,那么集群只需要将节点 A 、B、C 中的某些槽移动到节点 D 就可以了。
2 与此类似,如果用户要从集群中移除节点 A ,那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C ,然后再移除节点 A 就可以了。
因为槽在节点之间移动不会造成节点阻塞,所以无论是添加新节点还是移除已存在节点,又或者改变某个节点包含的哈希槽数量,都不会造成集群下线,redis 集群能保证槽的平滑移动。
跳转
RedisCluster 还有一个特性便是去中心化。客户端可以连接集群中的任意一个节点,集群中的任意一个节点都可对外提供服务。节点之间可共享集群配置(如槽的分配)。或者我们可以理解为,集群中的任意一个节点都是中心节点。假设有两个节点 A 和 B,客户端连接了 A 节点,并发起了一次请求 a,A 节点计算请求 a 的 key 得知该请求应该打到 B 节点上,然后 A 节点对请求 a 返回一个 MOVED B,通知客户端重定向到 B 节点。
- 当节点需要让一个客户端长期地(permanently)将针对某个槽的命令请求发送至另一个节点时, 节点向客户端返回 MOVED 转向
- 当节点需要让客户端仅仅在下一个命令请求中转向至另一个节点时, 节点向客户端返回 ASK 转向
- 如果一个槽被设置为 MIGRATING 状态时,原本持有该槽的节点会继续接受关于这个槽的命令请求,但只有当键存在于该节点时,节点才会处理这个请求。如果命令所使用的键不存在于该节点(有可能是一个新的键,也有可能是被迁移走的键),那么节点将向客户端返回一个 ASK 转向(redirection)错误,告知客户端,要将命令请求发送到槽的迁移目标节点,如果客户端接收到 ASK 转向, 那么将命令请求的发送对象调整为转向所指定的节点,先发送一个 ASKING 命令,然后再发送真正的命令请求。
- 如果一个槽被设置为 IMPORTING 状态时,节点仅在接收到 ASKING 命令之后,才会接受关于这个槽的命令请求。如果客户端向节点发送该槽的数据请求,命令为非 ASKING 时,那么节点会使用 MOVED 转向错误将命令请求转向至真正负责处理这个槽的节点。
当客户端收到MOVED错误后,可以使用CLUSTER NODES或CLUSTER SLOTS命令来更新整个集群的信息,因为当重定向发生时,很少会是单个槽位的变更,一般都会是多个槽位一起更新。因此,在收到MOVED错误时,客户端应该尽早更新集群的分布信息。当集群达到稳定状态时,客户端保存的槽位和节点的对应信息都是正确的,cluster的性能也会达到非常高效的状态
容错
- 所有的 redis 节点彼此互联 (PING-PONG 机制),内部使用二进制协议优化传输速度和带宽。
- 节点的 fail 是通过集群中超过半数的节点检测失效或者**某个节点主从全挂时才生效。
数据一致性问题
Redis集群尽可能保证数据的强一致性,但在特定条件下会丢失数据,原因有两点:异步replication机制以及network partition。
- Master以及对应的Slaves之间使用异步的机制:
第一个原因是因为集群是用了异步复制. 写操作过程:
1)客户端向主节点B写入一条命令。
2)主节点B向客户端回复命令状态。
3)主节点将写操作复制给他得从节点 B1, B2 和 B3。
在节点failover后,新的Master将会最终替代其他老的master:
write命令提交到Master,Master执行完毕后向Client返回“OK”;如果此时Master不可达的时间超过阀值,此时集群将触发对应的slave选举为新的Master,此时没有同步到slave的数据将丢失。
- 在network partition时,总有一个窗口期(node timeout)可能会导致数据丢失:
网络分区:网络分区指由于网络设备的failure,造成网络分裂为多个独立的组
由于网络分区,此时master不可达,且Client与Master处于一个分区(在同一个网络中,网络内部联通,但是外部不通),且此时集群处于“OK”。此时Failover机制,将其中一个Slave提升为新的Master,等待网络分区消除后,老的Master再次可达,之后Master节点将被切换为Slave,而在这段期间,处于网络分区期间,Client仍然将write提交到老的Master,因为该Master被认为是仍然有效的。当老的Master再次加入集群,被切换成Slave后,这些数据将永远丢失。
选举
选举过程是集群中所有 master 参与,如果半数以上 master 节点与当前 master 节点通信超时,则集群认为当前 master 节点挂掉.
什么时候整个集群不可用?当集群不可用时, 所有对集群的操作做都将失败。以下是会导致集群不可用的其中两种情况:
- a: 集群任意 master 挂掉,并且当前 master 没有 slave,集群不可用。
- b: 集群超过半数以上 master 挂掉,无论是否有 slave,集群不可用。
节点失效检测
以下是节点失效检查的实现方法:
- 当一个节点向另一个节点发送 PING 命令, 但是目标节点未能在给定的时限内返回 PING 命令的回复时, 那么发送命令的节点会将目标节点标记为 PFAIL (possible failure,可能已失效)。
等待 PING 命令回复的时限称为“节点超时时限(node timeout)”, 是一个节点选项(node-wise setting)。
- 每次当节点对其他节点发送 PING 命令的时候, 它都会随机地广播三个它所知道的节点的信息, 这些信息里面的其中一项就是说明节点是否已经被标记为 PFAIL 或者 FAIL 。
- 当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这称为失效报告(failure report)。
- 如果节点已经将某个节点标记为 PFAIL , 并且根据节点所收到的失效报告显式, 集群中的大部分其他主节点也认为那个节点进入了失效状态, 那么节点会将那个失效节点的状态标记为 FAIL 。
- 一旦某个节点被标记为 FAIL , 关于这个节点已失效的信息就会被广播到整个集群, 所有接收到这条信息的节点都会将失效节点标记为 FAIL 。
简单来说, 一个节点要将另一个节点标记为失效, 必须先询问其他节点的意见, 并且得到大部分主节点的同意才行。
集群状态检测
一旦配置处理完毕, 集群会进入以下两种状态的其中一种:
- FAIL : 集群不能正常工作。 当集群中有某个节点进入失效状态时, 集群不能处理任何命令请求, 对于每个命令请求, 集群节点都返回错误回复。
- OK : 集群可以正常工作, 负责处理全部 16384 个槽的节点中, 没有一个节点被标记为 FAIL 状态。
因为过期的失效报告会被移除, 所以主节点要将某个节点标记为 FAIL 的话, 必须以最近接收到的失效报告作为根据。
- 集群进入 FAIL 状态的两种情况:
- 至少有一个哈希槽不可用,因为负责处理这个槽的节点进入了 FAIL 状态,且该节点没有从节点。
- 集群中的大部分主节点都进入下线状态。当大部分主节点都进入 PFAIL 状态时,集群也会进入 FAIL 状态。
第二个检查是必须的, 因为要将一个节点从 PFAIL 状态改变为 FAIL 状态, 必须要有大部分主节点进行投票表决, 但是, 当集群中的大部分主节点都进入失效状态时, 单凭一个两个节点是没有办法将一个节点标记为 FAIL 状态的。
因此, 有了第二个检查条件, 只要集群中的大部分主节点进入了下线状态, 那么集群就可以在不请求这些主节点的意见下, 将某个节点判断为 FAIL 状态, 从而让整个集群停止处理命令请求。
从节点选举
- 节点是已下线Master对应的Slave
- FAIL状态的Master负责的hash slot 非空
- 主从节点之间的replication link断线的时长不能超过NODE_TIMEOUT * REDIS_CLUSTER_SLAVE_VALIDITY_MULT
当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:
- slave发现自己的master变为FAIL
- 将自己记录的集群currentEpoch加1,并广播
FAILOVER_AUTH_REQUEST
信息 - 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送
FAILOVER_AUTH_ACK
,对每一个epoch只发送一次ack - 尝试failover的slave收集
FAILOVER_AUTH_ACK
- 超过半数后变成新Master
- 广播Pong通知其他集群节点。
从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票。
延迟计算公式:DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK *1000ms
SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。
参考文章
https://www.infoq.cn/article/...
https://www.cnblogs.com/ivict...
http://redisdoc.com/topic/clu...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。