节点交互
所有节点总是允许接受来自cluster bus的连接请求,并且即使请求PING的节点是不可信的也会进行应答。然而,所有来自非集群节点的packets都会被忽略。
只有以下两种情况节点才会把其他节点认为是集群的一部分:
如果一个节点使用 MEET message 介绍自己。MEET message 命令是强制其他节点把自己当成是集群的一部分。只有系统管理员使用 CLUSTER MEET ip port 命令节点才会发送MEET message给其他节点。
另外一种方式就是通过集群节点间的推荐机制。例如 如果A节点知道B节点属于集群,而B知道C节点属于集群,那么B将会发送gossip信息告知A:C是属于集群的。当A获得gossip信息之后就会尝试去连接C。
这意味着,当我们以任意连接方式为集群加入一个节点,集群中所有节点都会自动对新节点建立信任连接。也就是说,集群具备自动识别所有节点的功能,但是这仅发生在当系统管理强制为新节点与集群中任意节点建立信任连接的前提下。
这个机制使得集群系统更加健壮。
当一个节点故障时,其余节点会尝试连接其他所有已知的节点已确定其他节点的健壮性。
被移动数据的重定向
Redis客户端被允许向集群中的任意节点发送命令,其中包括从节点。被访问的节点将会分析命令中所需要的数据(这里仅指请求单个key),并自己通过判断hash slot确定数据存储于哪个节点。
如果被请求节点拥有hash slot数据(这里指请求数据未被迁移过 或者 hash slot在数据迁移后被重新计算过),则会直接返回结果,否则将会返回一个 MOVED 错误。
MOVED 错误如下:
-MOVED 3999 127.0.0.1:6381
这个错误包括请求的数据所处的 hash slot(3999) 和 数据目前所属的IP:端口。客户端需要通过访问返回的IP:端口获取数据。即使在客户端请求并等待数据返回的过程中,集群配置已被更改(也就是说请求的key在配置文件中所属的节点ID已被重定向至新的IP:端口),目标节点依然会返回一个MOVED错误。
所以虽然在集群中的节点使用节点ID来确定身份,但是map依然是靠hash slot和Redis节点的IP:端口来进行配对。
客户端虽然不被要求但是应该尝试去记住hash slot 3999现在已被转移至127.0.0.1:6381。这样的话,当一个新的命令需要从hash slot 3999获取数据时就可以有更高的几率从hash slot获取到正确的目标节点。
假设集群已经足够的稳定(不增删节点),那么所有的客户端将会拥有一份hash slots对应节点的数据,这可以使整个集群更高效,因为这样每个命令都会直接定向到正确的节点,不需要通过节点寻找节点或者重新计算hash slot对应的节点。
集群不下线更新配置
Redis集群支持线上增删节点。实际上对于系统来说,增加和删除节点在本质上是一样的,因为他们都是把hash slot从一个节点迁移至另外一个节点而已。
增加节点:集群中加入一个空节点并且把hash slot从已存在的节点们移至新节点。
删除节点:集群删除一个已存在节点并且把hash slot分散到已存在的其他节点中。
所以实现这个功能的核心就是迁移slots。实际上从某种观点上来说,hash slot只不过是一堆key的合集,所以Redis集群要做的事情只是在重分片的时候把一堆key从一个实例移动到另外一个实例。
为了清楚的了解这是如何实现的,我们需要先了解一下CLUSTER用来控制slots传输的底层命令。
这些底层命令包括:
CLUSTER DELSLOTS slot1 [slot2] ... [slotN]
CLUSTER SETSLOT slot NODE node
CLUSTER SETSLOT slot MIGRATING node
CLUSTER SETSLOT slot IMPORTING node
前两个命令 ADDSLOTS 和 DELSLOTS 是用来在Redis节点上增加/删除slots。当hash slots被赋值之后他们会通过gossip协议在整个集群进行广播(例如:大喊一声 兄弟们 我现在住在X节点 有需要我的以后请到X节点来找我)。当slots被添加,ADDSLOTS 命令是用来通知集群其余所有节点最高效的方法。
SETSLOT 命令是用来给把slot注册给一个特殊的node ID(也就是说ADDSLOTS 和 DELSLOTS 对slots进行操作是不指定节点的 而SETSLOT 是会指定节点的)。另外 SETSLOT 还包含两个特殊的状态 MIGRATING 和 IMPORTING:
当一个slot是以 MIGRATING 状态进行设置,那么目标节点将在确认key存在的前提下接受这个hash slot的所有请求,否则查询会被使用 -ASK 重定向至源节点。
当一个slot是以 IMPORTING 状态进行设置,那么目标节点只接受被设置过ASKING命令的所有请求,否则查询将会通过 -MOVED错误重定向至真正的hash slot所有者。
(MIGRATING 和 IMPORTING 我自己也没太看懂 所以这里不敢保证翻译的没有问题)
当你第一次看到以上内容的时候或许会感到困惑,不过没关系,现在我们来把思路理清楚。假设我们有2个Redis节点,一个叫A,另一个叫B。现在我们希望把hash slot8 从A移动到B,那么我们执行的命令应该如下:
We send A: CLUSTER SETSLOT 8 MIGRATING B
所有来自客户端对hash slot8的查询每次都会被导向至节点A,实际过程如下:所有对A节点存在的数据查询会由A节点来处理;所有对A节点不存在的数据查询会由B节点来处理。
我们会发现我们将会无法在A节点创建任何新的数据,因为会被导向B节点。为了解决这个问题,我们设计了一个叫redis-trib的特殊客户端来保证把A节点所有存在的key迁移至B节点。
我们用以下命令来处理:
上面的命令将会返回hash slot中 count keys。对每一个key,redis-trib都会给A节点发送一个 MIGRATE 命令,这个命令会以一种原子的方式把key从A迁移到B(两个节点在迁移过程中都会被锁定)。
以下展示 MIGRATE 如何工作:
MIGRATE 命令会先连接目标节点,并把目标key序列化后进行传输,当源节点收到OK返回值后会删除源节点上的key删除。所以从这个观点上来看,一个key只能存在A或者B而不会同时存在与A和B。