Redis集群模式之Redis Cluster(1)
在前面的文章中我们介绍了什么是主从复制模式和哨兵模式,这两种模式虽然可以使用读写分离策略对读的并发性进行扩展,但是写能力和存储能力无法得到有效的扩展,只能是主节点服务器单服务器的性能上限。如果面对海量数据的存储,就要构建主节点之间的集群,同时要兼具上面两种模式的优点,也就是每个主节点必须有自己的从节点来进行数据冗余存储,这就是Redis Cluster模式。
Redis Cluster主要模块介绍
哈希槽(Hash Slot)
Redis Cluster没有使用一致性Hash,而是引入了哈希槽的概念。Redis Cluster中有16384(2^14)个哈希槽,每个key通过CRC16校验后对16383取模来决定放置到哪个槽。Cluster中的每个节点负责一部分Hash槽。
假设集群中存在3个主节点,则可能按照以下数量进行哈希槽的分配:
主节点1负责0~5400号的哈希槽,主节点2负责5401~10800号的哈希槽,主节点3负责10801~16383号的哈希槽。
Keys Hash Tags
Hash Tags提供了一种途径,用来将多个相关的key分配到相同的哈希槽中,这是Redis Cluster中实现multi-key操作的基础。
Hash Tag规则如下,如果满足如下规则,那么“{”和“}”之间的字符将用来计算Hash Slot,以保证这样的key储存在同一个哈希槽中。规则如下:
(1)key包含一个“{”字符;
(2)并且如果在这个“{”字符的右边还有一个“}”字符;
(3)并且如果在“{”和“}”字符之间存在至少一个字符。
举个例子:
{id888}.a和{id888}.b这两个key会被存放在同一个哈希槽中,因为哈希值在计算时只考虑“{}”内的值。如果不满足上面的三条规则,则不会启用Hash Tags。
Cluster Nodes属性
每个节点在Cluster中有一个唯一的名字。这个名字由160bit随机十六进制数字表示,并在节点启动时第一次获得(通常通过/dev/urandom)。节点在配置文件中保留他的ID,并永远使用这个ID,直到被管理员使用CLUSTER RESET HARD命令hard reset这个节点。
节点ID被用来在整个Cluster中标识每个节点。一个节点可以修改自己的IP地址而不需要修改自己的ID。Cluster可以检测到IP/port的改动并通过运行在Cluster Bus上的gossip协议重新配置该节点。
节点ID并不是唯一与节点绑定的信息,但是他是唯一的一个总是保持全局一致的字段。每个节点都拥有一系列相关的信息。一些信息是关于本节点在集群中配置的细节,并最终在Cluster内部保持一致。而其他信息,比如节点最后被ping的时间,是节点的本地信息。
每个节点维护着集群内其他节点的以下信息:节点id、节点的IP和Port、节点标签,主节点id(表明该节点为从节点),最后被挂起的ping的发送时间(如果没有挂起的ping则为0),最后一次收到pong的时间,当前的节点configuration epoch,链接状态,以及最后是该节点服务的哈希槽。
对节点字段更详细的描述,可以参考对命令CLUSTER NODES的描述。
Cluster总线
每个Redis Cluster节点有一个额外的TCP端口用来接受其他节点的连接。这个端口与用来接收Client命令的普通TCP端口有一个固定的offset。该端口等于普通命令端口+10000。
节点到节点的通信只使用集群总线,同时使用集群总线协议:有不同的类型和大小的帧组成的二进制协议。
集群拓扑
Redis Cluster是一张全网拓扑,节点与其他每个节点之间都保持着TCP连接。在一个拥有N个节点的集群中,每个节点由N-1个TCP传出连接,和N-1个TCP传入连接。这些TCP连接总是保持活性。当一个节点在集群总线上发送了ping请求并期待对方回复pong,如果没有得到回复,在等待足够长时间以便将对方标记为不可达之前,它将先尝试重新连接对方以刷新与对方的连接。而在全网拓扑中的Redis Cluster节点,节点使用gossip协议和配置更新机制来避免在正常情况下节点之间交换过多的消息,因此集群内交换的消息数目(相对节点数目)不是指数级的。
节点握手
节点总是接受集群总线端口的连接,并且总是会回复ping请求,即使ping来自一个不可信节点。然而,如果发送节点被认为不是当前集群的一部分,那么其他包将被抛弃。
节点认定其他节点是当前集群的一部分有两种方式:
(1)如果一个节点出现在一条MEET消息中。一条meet消息非常像一个ping消息,但是它会强制接收者接收一个节点作为集群的一部分。节点只有在接收到系统管理员的CLUSTER MEET ip port命令,才会向其他节点发送MEET消息。
(2)如果一个被信任的节点gossip了某个节点,那么接收到gossip消息的节点也会将那个节点标记为集群的一部分。也就是说,如果在集群中,A知道B,B知道C,B发送gossip消息到A,告诉A:C是集群的一部分。这时,A会把C注册为网络的一部分,并尝试与C建立连接。
这就意味着,一旦我们把某个节点加入了连接图,它们最终会自动形成一张全连接图。这意味着只要系统管理员强制加入一条信任关系,集群可以自动发现发现其他节点。
请求重定向
Redis Cluster采用去中心化的架构,集群的主节点各自负责一部分哈希槽,客户端如何确定key到底会映射到哪个节点上呢?这就是下面我们要介绍的请求重定向。
在Cluster模式下,节点对请求的处理过程如下:
(1)检查当前key是否存在当前的节点下(通过CRC16校验后取模,得到对应的Slot值;查询该Slot值负责的节点,得到节点指针;将得到的指针与自身进行比较);
(2)如果Slot不是自身负责的,则返回MOVED重定向;
(3)如果Slot是自身负责的,并且key在Slot中,则返回该key对应的结果;
(4)如果key不在此Slot中,检查该Slot是否正在迁出(MIGRATING);
(5)如果key正在迁出,返回ASK错误,重定向客户端到迁移的目的服务器上;
(6)如果Slot未迁出,检查Slot是否导入中;
(7)如果Slot导入中且有ASKING标记,则直接操作;
(8)否则返回MOVED重定向。
我们来讲解一下什么是MOVED重定向和ASK重定向。
MOVED重定向
MOVED具体的流程如下:
(1)客户端发送键命令给任意节点;
(2)任意节点对键进行Slot值的计算并得到对应节点的指针;
(3)接着判断得到的指针是否指向自身,并判断是否槽命中。如果是,则执行命令,如果不是,则返回MOVED重定向命令给客户端。
(4)客户端根据重定向重新发送键命令到对应节点。
槽命中:指的是所找的键在当前节点负责的哈希槽中可以找到。
ASK重定向
ASK重定向发生于集群伸缩(集群中节点数量扩充或缩减)时,集群伸缩会导致哈希槽的迁移,当我们去源节点访问时,此时的数据可能已经迁移到了目标节点,使用ASK重定向来解决此问题。
Smart客户端
上述两种重定向机制使得客户端的实现更加复杂,通过Smart客户端(Jedis Cluster)来减低复杂性,追求更好的性能。客户端内部负责计算/维护键槽
节点的映射,用于快速定位目标节点。具体的实现原理如下:
(1)从集群中选取一个可运行节点,使用Cluster Slots得到槽和节点的映射关系。
(2)将上述映射关系储存到本地,通过映射关系就可以直接对目标节点进行操作(CRC16(key)Slot
Node),很好地避免了MOVED重定向,并且为每个节点创建Jedis Pool。
至此,客户端就可以用来命令操作了,具体的流程图如下:
本篇文章我们主要介绍了Redis Cluster要解决的问题、Redis Cluster中的一些基本概念和Redis Cluster中的重定向机制,大家有什么问题或者勘误可以在评论区留言,笔者看到都会回复的。