Redis 集群模式 Redis Cluster

Redis Cluster是一个高性能高可用的分布式系统。由多个Redis实例组成的整体,数据按照Slot(槽位)存储分布在多个Redis实例上,通过Gossip(流言)协议来进行节点之间通信。
重要概念:
槽:
Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权
数据分片:
在Redis Cluster中,拥有16384个slot,这个数是固定的,存储在Redis Cluster中的所有的键都会被映射到这些slots中。
数据库中的每个键都属于这16384个哈希槽的其中一个,集群使用公式CRC16(key) % 16384来计算键key属于哪个槽,其中CRC16(key)语句用于计算键key的CRC16校验和。集群中的每个节点负责处理一部分哈希槽
Redis 集群是一个网状结构,每个节点都通过 TCP 连接跟其他每个节点连接。在一个有 N 个节点的集群中,每个节点都有 N-1 个流出的 TCP 连接,和 N-1 个流入的连接,这些 TCP 连接会永久保持。
节点的互相发现有2种方式:
1) 管理员显示发送 CLUSTER MEET IP PORT 命令,告诉集群有新节点加入
2) 如果一个可信节点向另一个节点传播第三者节点的信息, 那么接收信息的那个节点也会将第三者节点识别为集群中的一份子。 也即是说, 如果 A 认识 B , B 认识 C , 并且 B 向 A 传播关于 C 的信息, 那么 A 也会将 C 识别为集群中的一份子, 并尝试连接C
这种节点识别机制通过防止不同的 Redis 集群因为 IP 地址变更或者其他网络事件的发生而产生意料之外的联合(mix), 从而使得集群更具健壮性。
客户端查找指令出现错误:MOVED
一个 Redis 客户端可以向集群中的任意节点(包括从节点)发送命令请求。 节点会对命令请求进行分析, 如果该命令是集群可以执行的命令, 那么节点会查找这个命令所要处理的键所在的槽。
如果要查找的哈希槽正好就由接收到命令的节点负责处理, 那么节点就直接执行这个命令;
另一方面, 如果所查找的槽不是由该节点处理的话, 节点将查看自身内部所保存的哈希槽到节点 ID 的映射记录, 并向客户端回复一个 MOVED 错误。
GET IsJackHandsome
-MOVED 3999 127.0.0.1:6381
3999 代表 IsJackHandsome 的哈希槽,后面是负责这个槽的节点的IP和端口号。客户端需要再向这个节点发送GET IsJackHandsome,如果有就会YES,如果他因为等了很久导致节点不再处理 3999的槽了,该节点会再次返回MOVED错误,以及现在负责处理3999槽的节点。客户端应该记录下槽点由哪个节点负责处理,下一次可以加快寻找正确节点的速度。
集群节点的添加和删除
 节点的添加操作和节点的删除操作可以抽象成同一个操作,那就是, 将哈希槽从一个节点移动到另一个节点:
  • 添加一个新节点到集群, 等于将其他已存在节点的槽移动到一个空白的新节点里面。
  • 从集群中移除一个节点, 等于将被移除节点的所有槽移动到集群的其他节点上面去。
相关命令:
  • CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]
  • CLUSTER DELSLOTS slot1 [slot2] ... [slotN]
  • CLUSTER SETSLOT slot NODE node
  • CLUSTER SETSLOT slot MIGRATING node
  • CLUSTER SETSLOT slot IMPORTING node
最开头的两条命令 ADDSLOTS 和 DELSLOTS 分别用于向节点指派(assign)或者移除节点, 当槽被指派或者移除之后, 节点会将这一信息通过 Gossip 协议传播到整个集群。 ADDSLOTS 命令通常在新创建集群时, 作为一种快速地将各个槽指派给各个节点的手段来使用。
CLUSTER SETSLOT slot NODE node 子命令可以将指定的槽 slot 指派给节点 node 。
至于 CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOT slot IMPORTING node 命令, 前者用于将给定节点 node 中的槽 slot 迁移出节点, 而后者用于将给定槽 slot 导入到节点 node
如果命令所使用的键不存在于该节点, 那么节点将向客户端返回一个 -ASK 转向(redirection)错误, 告知客户端, 要将命令请求发送到槽的迁移目标节点
当一个槽被设置为IMPORTING 状态时,节点仅在收到ASKING命令之后,才会接受关于这个槽的命令请求。
如果客户端没有向节点发送ASKING命令,那么节点会使用-MOVED转向错误将命令请求专项至真正负责处理这个槽的节点。
假设现在, 我们有 A 和 B 两个节点, 并且我们想将槽 8 从节点 A 移动到节点 B , 于是我们:
  • 向节点 B 发送命令 CLUSTER SETSLOT 8 IMPORTING A
  • 向节点 A 发送命令 CLUSTER SETSLOT 8 MIGRATING B
每当客户端向其他节点发送关于哈希槽 8 的命令请求时, 这些节点都会向客户端返回指向节点 A 的转向信息:
  • 如果命令要处理的键已经存在于槽 8 里面, 那么这个命令将由节点 A 处理。
  • 如果命令要处理的键未存在于槽 8 里面(比如说,要向槽添加一个新的键), 那么这个命令由节点 B 处理。
这种机制将使得节点 A 不再创建关于槽 8 的任何新键。
与此同时, 一个特殊的客户端 redis-trib 以及 Redis 集群配置程序(configuration utility)会将节点 A 中槽 8 里面的键移动到节点 B 。
键的移动
CLUSTER GETKEYSINSLOT slot count 让节点返回 count 个 slot 槽中的键, 对于命令所返回的每个键, redis-trib 都会向节点 A 发送一条 
MIGRATE host port key destination-db timeout [COPY] [REPLACE] 命令, 该命令会将所指定的键原子地(atomic)从节点 A 移动到节点 B (在移动键期间,两个节点都会处于阻塞状态,以免出现竞争条件)。
这个命令会连接到destination-db节点,并将序列化后的key数据 发送给destination-db,一但返回ok 节点就将自已的key从数据库中删除。
ASK 转向
比如说, 在我们上一节列举的槽 8 的例子中, 因为槽 8 所包含的各个键分散在节点 A 和节点 B 中, 所以当客户端在节点 A 中没找到某个键时, 它应该转向到节点 B 中去寻找, 但是这种转向应该仅仅影响一次命令查询, 而不是让客户端每次都直接去查找节点 B : 在节点 A 所持有的属于槽 8 的键没有全部被迁移到节点 B 之前, 客户端应该先访问节点 A , 然后再访问节点 B 
因为上述原因, 如果我们要在查找节点 A 之后, 继续查找节点 B , 那么客户端在向节点 B 发送命令请求之前, 应该先发送一个 ASKING 命令, 否则这个针对带有 IMPORTING 状态的槽的命令请求将被节点 B 拒绝执行。
接收到客户端 ASKING 命令的节点将为客户端设置一个一次性的标志(flag), 使得客户端可以执行一次针对 IMPORTING 状态的槽的命令请求
因为上述原因, 如果我们要在查找节点 A 之后, 继续查找节点 B , 那么客户端在向节点 B 发送命令请求之前, 应该先发送一个 ASKING 命令, 否则这个针对带有 IMPORTING 状态的槽的命令请求将被节点 B 拒绝执行。
接收到客户端 ASKING 命令的节点将为客户端设置一个一次性的标志(flag), 使得客户端可以执行一次针对 IMPORTING 状态的槽的命令请求。
从客户端的角度来看, ASK 转向的完整语义(semantics)如下:
  • 如果客户端接收到 ASK 转向, 那么将命令请求的发送对象调整为转向所指定的节点。
  • 先发送一个 ASKING 命令,然后再发送真正的命令请求。
  • 不必更新客户端所记录的槽 8 至节点的映射: 槽 8 应该仍然映射到节点 A , 而不是节点 B 。
一旦节点 A 针对槽 8 的迁移工作完成, 节点 A 在再次收到针对槽 8 的命令请求时, 就会向客户端返回 MOVED 转向, 将关于槽 8 的命令请求长期地转向到节点 B 。
注意, 即使客户端出现 Bug , 过早地将槽 8 映射到了节点 B 上面, 但只要这个客户端不发送 ASKING 命令, 客户端发送命令请求的时候就会遇上 MOVED 错误, 并将它转向回节点 A 
容错
节点失效检测
以下是节点失效检查的实现方法:
  • 当一个节点向另一个节点发送 PING 命令, 但是目标节点未能在给定的时限内返回 PING 命令的回复时, 那么发送命令的节点会将目标节点标记为 PFAIL (possible failure,可能已失效)。
等待 PING 命令回复的时限称为“节点超时时限(node timeout)”, 是一个节点选项(node-wise setting)。
  • 每次当节点对其他节点发送 PING 命令的时候, 它都会随机地广播三个它所知道的节点的信息, 这些信息里面的其中一项就是说明节点是否已经被标记为 PFAIL 或者 FAIL 。
  • 当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这称为失效报告(failure report)。
  • 如果节点已经将某个节点标记为 PFAIL , 并且根据节点所收到的失效报告显式, 集群中的大部分其他主节点也认为那个节点进入了失效状态, 那么节点会将那个失效节点的状态标记为 FAIL 。
  • 一旦某个节点被标记为 FAIL , 关于这个节点已失效的信息就会被广播到整个集群, 所有接收到这条信息的节点都会将失效节点标记为 FAIL 。
简单来说, 一个节点要将另一个节点标记为失效, 必须先询问其他节点的意见, 并且得到大部分主节点的同意才行。
因为过期的失效报告会被移除, 所以主节点要将某个节点标记为 FAIL 的话, 必须以最近接收到的失效报告作为根据。
在以下两种情况中, 节点的 FAIL 状态会被移除:
  • 如果被标记为 FAIL 的是从节点, 那么当这个节点重新上线时, FAIL 标记就会被移除。
保持(retaning)从节点的 FAIL 状态是没有意义的, 因为它不处理任何槽, 一个从节点是否处于 FAIL 状态, 决定了这个从节点在有需要时能否被提升为主节点。
  • 如果一个主节点被打上 FAIL 标记之后, 经过了节点超时时限的四倍时间, 再加上十秒钟之后, 针对这个主节点的槽的故障转移操作仍未完成, 并且这个主节点已经重新上线的话, 那么移除对这个节点的 FAIL 标记。
在第二种情况中, 如果故障转移未能顺利完成, 并且主节点重新上线, 那么集群就继续使用原来的主节点, 从而免去管理员介入的必要。
从节点选举
一旦某个主节点进入 FAIL 状态, 如果这个主节点有一个或多个从节点存在, 那么其中一个从节点会被升级为新的主节点, 而其他从节点则会开始对这个新的主节点进行复制。
新的主节点由已下线主节点属下的所有从节点中自行选举产生, 以下是选举的条件
1)这个节点是已下线主节点的从节点
2)已下线主节点负责处理槽数量非空
3)从节点的数据被认为是可靠的,也即是,主从节点之间的复制链接(replication link)的断线时长不能超过节点超时时限(node timeout 15s)乘以REDIS_CLUSTER_SLAVE_VALIDITY_MULT (10) 常量得出的积(断线时长不能超过150s)
如果一个从节点满足了以上的所有条件,那么这个从节点将向集群中的其他主节点发送授权请求,询问它们,是否允许自己(从节点)升级为新的主节点。
如果发送授权请求的从节点满足以下属性, 那么主节点(其他主节点)将向从节点返回 FAILOVER_AUTH_GRANTED 授权, 同意从节点的升级要求:
  • 发送授权请求的是一个从节点, 并且它所属的主节点处于 FAIL 状态。
  • 在已下线主节点的所有从节点中, 这个从节点的节点 ID 在排序中是最小的。
  • 这个从节点处于正常的运行状态: 它没有被标记为 FAIL 状态, 也没有被标记为 PFAIL 状态。
一旦某个从节点在给定的时限内得到大部分主节点的授权, 它就会开始执行以下故障转移操作:
  • 通过 PONG 数据包(packet)告知其他节点, 这个节点现在是主节点了。(上来先拜码头)
  • 通过 PONG 数据包告知其他节点, 这个节点是一个已升级的从节点(promoted slave)。
  • 接管(claiming)所有由已下线主节点负责处理的哈希槽。(老九门谁干掉老大谁就接管他的地盘)
  • 显式地向所有节点广播一个 PONG 数据包, 加速其他节点识别这个节点的进度, 而不是等待定时的 PING / PONG 数据包
所有其他节点都会根据新的主节点对配置进行相应的更新,特别地:
  • 所有被新的主节点接管的槽会被更新。(地盘更名改姓了,我的地盘有我的规则)
  • 已下线主节点的所有从节点会察觉到 PROMOTED 标志, 并开始对新的主节点进行复制。
  • 如果已下线的主节点重新回到上线状态, 那么它会察觉到 PROMOTED 标志, 并将自身调整为现任主节点的从节点。
总结:
redis集群有16384个槽,每台服务器可以负责不同的槽。
redis-cluster 数据分片的规则是crc16(key)
redis cluster 集群是一个网状结构,每个节点都通过 TCP 连接跟其他每个节点连接。
节点发现有2种方式,一是管理员显示插入,二是自己相熟的节点介绍。
MOVED是永久的把某槽的节点转移到其他节点,ASK 是在访问有IMPORTING状态下的节点,先行的访问。
集群节点的操作本质是移动哈希槽。
PFAIL(possible fail) 可能fail,指单节点发现某节点超时未响应pong;当大部分的节点认为某节点PFAIL 则节点变为FAIL;FAIL之后所有集群都知道;
从节点选举为主节点,条件有三:1 是原主节点的从节点,
2 原主节点负责的槽点不能为空
3 从节点是可靠的
从节点选举过程:请求其他主节点授权,其他主节点物色最中意的从节点,得到大部分主节点的授权;
故障转移: 通过PONG包告诉其他节点自己是一个从-从节点-升上来的主节点,接管原主节点的hash槽,加速和别人主节点的认识。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注