Redis集群 详解

什么是Redis集群?
Redis集群是一个分布式、容错的实现,集群可以使用的功能是单机Redis功能的子集。
Redis集群中不存在中心节点与代理节点,集群一个主要的设计目标就是达到线性可扩展性。
Redis集群为了保证数据一致性而牺牲了部分容错性(系统在保证对网络断线和节点怠机具有有限抵抗力的前提下,尽可能保持数据的一致性)。
 
容错的实现:
Redis集群的容错功能是通过使用主从节点机制来实现的。
1)主节点和从节点使用完全相同的服务器实现,他们具有一样的功能,从节点在主节点失效时可以替换主节点。
2)如果不需要保证“先写入,后读取”操作的一致性(read-after-write consistency), 那么可以使用从节点来执行只读查询
 
功能子集:
Redis集群实现了单机redis中,所有处理单个数据库健的命令。
目前那种针对多个数据库健的复杂计算存在还没有被实现。
 
Redis集群协议
Redis节点责任:
1.持有键值对数据
2.记录集群的姿态,包括健到正确节点的映射
3.自动发现集群中的其他节点,识别工作不正常的节点,并在有需要的时候,选举出新的主节点
 
集群连接:
为了履行节点责任,集群每一个节点都与其他节点建立了集群连接,该连接为TCP连接,使用二进制协议进行通讯。
鞋垫之间使用Gossip 协议来进行下列工作:
1)传播关于集群的信息,以此来发现新的节点
2)向其他节点发送PING数据包,来检测目标节点的运行状态
3)在特定事件发生时,发生集群信息
4)在集群中发布或者订阅信息
注意:
因为Redis集群节点不能代理命令请求,所以客户端需要在节点返回-MOVED或者-ASK转向错误的时候,将命令请求转发到其它节点。
 
键发布模型:
Redis集群的键空间被分割为16384个槽(slot),集群的最大节点数也是16384(但是不推荐这么多,推荐最大节点数量为1000)。
每一个主节点都负责处理这些哈希槽的其中一部分。
当集群处于稳定状态(集群没在进行重配置存在-将某些槽从一个节点移动到另一个节点)时,每一个哈希槽都只由一个节点进行处理。
主节点可以有任意多的从节点,这些从节点在主节点断线或者怠机时,选举新的主节点进行替换。
 
槽映射算法:
HASH_SLOT = CRC16(key) mod 16384
以下是该算法所使用的参数:
算法的名称: XMODEM (又称 ZMODEM 或者 CRC-16/ACORN)
结果的长度: 16 位
多项数(poly): 1021 (也即是 x16 + x12 + x5 + 1)
初始化值: 0000
反射输入字节(Reflect Input byte): False
发射输出 CRC (Reflect Output CRC): False
用于 CRC 输出值的异或常量(Xor constant to output CRC): 0000
该算法对于输入 "123456789" 的输出: 31C3
 
集群节点属性:
每一个节点在集群中都有一个独一无二的ID,这个ID是一个十六进制的160位随机数,在节点第一次启动时由/dev/urandom生成。
节点将这个ID保持在配置文件中,只有配置文件在,节点将会一直沿用这个ID。
节点 ID 用于标识集群中的每个节点。 一个节点可以改变它的 IP 和端口号, 而不改变节点 ID 。 集群可以自动识别出 IP/端口号的变化, 并将这一信息通过 Gossip 协议广播给其他节点知道。
 
以下是每个节点都有的关联信息, 并且节点会将这些信息发送给其他节点:
1.节点所使用的 IP 地址和 TCP 端口号。
2.节点的标志(flags)。
3.节点负责处理的哈希槽。
4.节点最近一次使用集群连接发送 PING 数据包(packet)的时间。
5.节点最近一次在回复中接收到 PONG 数据包的时间。
6.集群将该节点标记为下线的时间。
7.该节点的从节点数量。
8.如果该节点是从节点的话,那么它会记录主节点的节点 ID 。 如果这是一个主节点的话,那么主节点 ID 这一栏的值为 0000000 。
以上信息的其中一部分可以通过向集群中的任意节点(主节点或者从节点都可以)发送 CLUSTER NODES 命令来获得。
 
节点握手:
节点总是应答(accept)来自集群连接端口的连接请求,并对接收到的PING数据包进行回复,即使这个PING数据包来自不可信的节点。但是除了PING,节点会拒绝所有来自非集群节点的数据包。
要让节点承认另一个节点属于同一个集群,有下面两种方法:
1)一个节点可以通过向另一个节点发送 MEET 信息, 来强制让接收信息的节点承认发送信息的节点为集群中的一份子。 一个节点仅在管理员显式地向它发送 CLUSTER MEET ip port 命令时, 才会向另一个节点发送 MEET 信息。
2) 如果一个可信节点向另一个节点传播第三者节点的信息, 那么接收信息的那个节点也会将第三者节点识别为集群中的一份子。 也即是说, 如果 A 认识 B , B 认识 C , 并且 B 向 A 传播关于 C 的信息, 那么 A 也会将 C 识别为集群中的一份子, 并尝试连接 C 。
 
MOVED转向:
一个 Redis 客户端可以向集群中的任意节点(包括从节点)发送命令请求。 节点会对命令请求进行分析, 如果该命令是集群可以执行的命令, 那么节点会查找这个命令所要处理的键所在的槽。
如果要查找的哈希槽正好就由接收到命令的节点负责处理, 那么节点就直接执行这个命令。
另一方面, 如果所查找的槽不是由该节点处理的话, 节点将查看自身内部所保存的哈希槽到节点 ID 的映射记录, 并向客户端回复一个 MOVED 错误。
 
重配置:
Redis 集群支持在集群运行的过程中添加或者移除节点。
实际上, 节点的添加操作和节点的删除操作可以抽象成同一个操作, 那就是, 将哈希槽从一个节点移动到另一个节点:
添加一个新节点到集群, 等于将其他已存在节点的槽移动到一个空白的新节点里面。
从集群中移除一个节点, 等于将被移除节点的所有槽移动到集群的其他节点上面去。
因此, 实现 Redis 集群在线重配置的核心就是将槽从一个节点移动到另一个节点的能力。 因为一个哈希槽实际上就是一些键的集合, 所以 Redis 集群在重哈希(rehash)时真正要做的, 就是将一些键从一个节点移动到另一个节点。
要理解 Redis 集群如何将槽从一个节点移动到另一个节点, 我们需要对 CLUSTER 命令的各个子命令进行介绍, 这些命理负责管理集群节点的槽转换表(slots translation table)。
命令表:
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 :
当一个槽被设置为 MIGRATING 状态时, 原来持有这个槽的节点仍然会继续接受关于这个槽的命令请求, 但只有命令所处理的键仍然存在于节点时, 节点才会处理这个命令请求。
如果命令所使用的键不存在与该节点, 那么节点将向客户端返回一个 -ASK 转向(redirection)错误, 告知客户端, 要将命令请求发送到槽的迁移目标节点。
当一个槽被设置为 IMPORTING 状态时, 节点仅在接收到 ASKING 命令之后, 才会接受关于这个槽的命令请求。
如果客户端没有向节点发送 ASKING 命令, 那么节点会使用 -MOVED 转向错误将命令请求转向至真正负责处理这个槽的节点。
 
ASK转向:
前面的MOVED转向是告诉客户端长期的将针对某一个槽的命令请求发送到另一个节点中。
而ASK是告诉客户端只是下一次请求发送到另一个节点中。
 
容错:
节点失效检测:
1.当一个节点向另一个节点发送 PING 命令, 但是目标节点未能在给定的时限内返回 PING 命令的回复时, 那么发送命令的节点会将目标节点标记为 PFAIL (possible failure,可能已失效)。
2.等待 PING 命令回复的时限称为“节点超时时限(node timeout)”, 是一个节点选项(node-wise setting)。
3.每次当节点对其他节点发送 PING 命令的时候, 它都会随机地广播三个它所知道的节点的信息, 这些信息里面的其中一项就是说明节点是否已经被标记为 PFAIL 或者 FAIL 。
4.当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这称为失效报告(failure report)。
5.如果节点已经将某个节点标记为 PFAIL , 并且根据节点所收到的失效报告显式, 集群中的大部分其他主节点也认为那个节点进入了失效状态, 那么节点会将那个失效节点的状态标记为 FAIL 。
6.一旦某个节点被标记为 FAIL , 关于这个节点已失效的信息就会被广播到整个集群, 所有接收到这条信息的节点都会将失效节点标记为 FAIL 。
总结:
一个节点只能将节点标记为PFAIL,需要先询问其他节点意见,并得到大部分节点同意之后才能将节点标记为FAIL。
 
节点恢复检测:
1.从节点重新上线,FAIL标记会直接被移除。
2.主节点在槽的故障迁移没有完成之前重新上线,将会移除这个节点的FAIL标记,并且继续以它为主节点。
 
从节点选举:
一旦某一个主节点进入FAIL状态,如果这个主节点有从节点存在,那么其中一个从节点将会升级为新的主节点,其它的从节点将开始对新的主节点进行复制。
新的主节点由所有的从节点选举产生,以下是选举的条件:
1)为下线节点的从节点
2)已下线的主节点负责处理的槽数量非空
3)从节点的数据被认为是可靠的(与原主节点之间复制链接的断开时间没有超过节点超时时限*常量REDIS_CLUSTER_SLAVE_VALIDITY_MULT)
 
如果一个从节点满足了以上的所有条件, 那么这个从节点将向集群中的其他主节点发送授权请求, 询问它们, 是否允许自己(从节点)升级为新的主节点。
 
如果发送授权请求的从节点满足以下属性, 那么主节点将向从节点返回 FAILOVER_AUTH_GRANTED 授权, 同意从节点的升级要求:
发送授权请求的是一个从节点, 并且它所属的主节点处于 FAIL 状态。
在已下线主节点的所有从节点中, 这个从节点的节点 ID 在排序中是最小的。
这个从节点处于正常的运行状态: 它没有被标记为 FAIL 状态, 也没有被标记为 PFAIL 状态。
 
一旦某个从节点在给定的时限内得到大部分主节点的授权, 它就会开始执行以下故障转移操作:
通过 PONG 数据包(packet)告知其他节点, 这个节点现在是主节点了。
通过 PONG 数据包告知其他节点, 这个节点是一个已升级的从节点(promoted slave)。
接管(claiming)所有由已下线主节点负责处理的哈希槽。
显式地向所有节点广播一个 PONG 数据包, 加速其他节点识别这个节点的进度, 而不是等待定时的 PING / PONG 数据包。
所有其他节点都会根据新的主节点对配置进行相应的更新,
 
特别地:
所有被新的主节点接管的槽会被更新。
已下线主节点的所有从节点会察觉到 PROMOTED 标志, 并开始对新的主节点进行复制。
如果已下线的主节点重新回到上线状态, 那么它会察觉到 PROMOTED 标志, 并将自身调整为现任主节点的从节点。
在集群的生命周期中, 如果一个带有 PROMOTED 标识的主节点因为某些原因转变成了从节点, 那么该节点将丢失它所带有的 PROMOTED 标识。
 
作者:红雨
出处:https://www.cnblogs.com/52why
微信公众号: 红雨python
原文地址:https://www.cnblogs.com/52why/p/14357840.html