redis cluster学习笔记

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 转向
  1. 如果一个槽被设置为 MIGRATING 状态时,原本持有该槽的节点会继续接受关于这个槽的命令请求,但只有当键存在于该节点时,节点才会处理这个请求。如果命令所使用的键不存在于该节点(有可能是一个新的键,也有可能是被迁移走的键),那么节点将向客户端返回一个 ASK 转向(redirection)错误,告知客户端,要将命令请求发送到槽的迁移目标节点,如果客户端接收到 ASK 转向, 那么将命令请求的发送对象调整为转向所指定的节点,先发送一个 ASKING 命令,然后再发送真正的命令请求。
  2. 如果一个槽被设置为 IMPORTING 状态时,节点仅在接收到 ASKING 命令之后,才会接受关于这个槽的命令请求。如果客户端向节点发送该槽的数据请求,命令为非 ASKING 时,那么节点会使用 MOVED 转向错误将命令请求转向至真正负责处理这个槽的节点。
当客户端收到MOVED错误后,可以使用CLUSTER NODES或CLUSTER SLOTS命令来更新整个集群的信息,因为当重定向发生时,很少会是单个槽位的变更,一般都会是多个槽位一起更新。因此,在收到MOVED错误时,客户端应该尽早更新集群的分布信息。当集群达到稳定状态时,客户端保存的槽位和节点的对应信息都是正确的,cluster的性能也会达到非常高效的状态

容错

  1. 所有的 redis 节点彼此互联 (PING-PONG 机制),内部使用二进制协议优化传输速度和带宽。
  2. 节点的 fail 是通过集群中超过半数的节点检测失效或者**某个节点主从全挂时才生效。

数据一致性问题

Redis集群尽可能保证数据的强一致性,但在特定条件下会丢失数据,原因有两点:异步replication机制以及network partition

  1. Master以及对应的Slaves之间使用异步的机制:
第一个原因是因为集群是用了异步复制. 写操作过程:
1)客户端向主节点B写入一条命令。
2)主节点B向客户端回复命令状态。
3)主节点将写操作复制给他得从节点 B1, B2 和 B3。

在节点failover后,新的Master将会最终替代其他老的master:
write命令提交到Master,Master执行完毕后向Client返回“OK”;如果此时Master不可达的时间超过阀值,此时集群将触发对应的slave选举为新的Master,此时没有同步到slave的数据将丢失。

  1. 在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 状态的两种情况:
  1. 至少有一个哈希槽不可用,因为负责处理这个槽的节点进入了 FAIL 状态,且该节点没有从节点。
  2. 集群中的大部分主节点都进入下线状态。当大部分主节点都进入 PFAIL 状态时,集群也会进入 FAIL 状态。

第二个检查是必须的, 因为要将一个节点从 PFAIL 状态改变为 FAIL 状态, 必须要有大部分主节点进行投票表决, 但是, 当集群中的大部分主节点都进入失效状态时, 单凭一个两个节点是没有办法将一个节点标记为 FAIL 状态的。
因此, 有了第二个检查条件, 只要集群中的大部分主节点进入了下线状态, 那么集群就可以在不请求这些主节点的意见下, 将某个节点判断为 FAIL 状态, 从而让整个集群停止处理命令请求。

从节点选举

  1. 节点是已下线Master对应的Slave
  2. FAIL状态的Master负责的hash slot 非空
  3. 主从节点之间的replication link断线的时长不能超过NODE_TIMEOUT * REDIS_CLUSTER_SLAVE_VALIDITY_MULT

当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:

  1. slave发现自己的master变为FAIL
  2. 将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息
  3. 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack
  4. 尝试failover的slave收集FAILOVER_AUTH_ACK
  5. 超过半数后变成新Master
  6. 广播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...

你可能感兴趣的:(redis,redis-cluster)