redis-cluster重定向和重分片

MOVED 重定向

一个 Redis 客户端可以自由地向集群中的任意节点(包括slave节点)发送请求。接收的节点会分析请求,如果这个命令是集群可以执行的(就是查询中只涉及一个键,或者多键在同一个哈希槽),节点会找出这个键/这些键所属的哈希槽对应的节点。

如果哈希槽在这个节点上,那么这个请求就简单地执行了。否则这个节点会查看它内部的哈希槽节点映射,然后给客户端返回一个MOVED错误,如下:

GET x

-MOVED 3999 127.0.0.1:6381

这个错误包括键的哈希槽和能处理这个查询的节点的IP:端口。客户端需要重新发送请求到给定IP 地址和端口号的节点。 注意:即使客户端在重发请求之前等待了很长一段时间,与此同时集群的配置信息发生改变,如果哈希槽3999现在是归属其他节点,那么目标节点会再向客户端回复一个MOVED错误。如果连接的节点没有信息变更,会重复这样。

从集群的角度看,节点是以 ID来标识的。我们尝试简化接口,所以只向客户端暴露哈希槽和用 IP:端口来标识的Redis节点之间的映射。

虽然并没有要求,但是客户端应该尝试记住哈希槽3999归属于127.0.0.1:6381。这样的话,一旦有一个新的命令需要发送,它能计算出目标键的哈希槽,找到正确节点的机率更高。

另一种方法是,当遇到一个MOVED时,使用CLUSTER NODES 或CLUSTER SLOTS命令刷新整个客户端集群布局。当遇到重定向,很可能多个插槽进行重新配置,而不是只有一个,所以尽快更新客户端的配置往往是最好的策略。

注意,当集群是稳定的时候(配置没有在变更),所有客户端最终都会得到一份哈希槽->节点 的映射表,这样能使得集群效率非常高,客户端直接定位目标节点,不用重定向、代理或发生其他单点故障。

一个客户端也应该能处理后文提到的 -ASK 重定向错误,否则不是一个完整的Redis集群客户端。
 

集群在线重新配置

Redis集群支持在集群运行过程中添加或移除节点。实际上,添加或移除节点都被抽象为同一个操作,那就是把哈希槽从一个节点移到另一个节点。这意味着相同的原理能用来重新平衡集群,增/删节点,等等。
 

  • 向集群添加一个新节点,就是把一个空节点加入到集群中并把某些哈希槽从已存在的节点移到新节点上;
  • 从集群中移除一个节点,就是把该节点上的哈希槽移到其他已存在的节点上;
  • 重新平衡,就是指向一堆哈希槽在节点间迁移。


所以实现这个的核心是能把哈希槽移来移去。从实际角度看,哈希槽就只是一堆键,所以 Redis 集群在重组分片时做的就是把键从一个节点移到另一个节点。移动一个哈希槽就是移动属于这个槽的所有键。为了理解这是怎么工作的,我们需要介绍 CLUSTER 的子命令,这些命令是用来操作 Redis 集群节点上的哈希槽转换表。

有以下子命令(在这个案例中有的没用到):
 

  • 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,就是简单地用来给Redis 节点指派或移除哈希槽。指派哈希槽就是告诉一个master节点它会负责存储和服务指定的哈希槽。

在哈希槽被指派后会将这个消息通过 gossip 协议向整个集群传播(协议在后文配置传播章节说明)。

ADDSLOTS 命令通常是用于在一个集群刚建立的时候从新给所有master节点指派哈希槽,总共有16384个。

DELSLOTS主要用于群集配置的手工修改或用于调试任务:在实践中很少使用。

如果SETSLOT 节点的形式被使用,SETSLOT用于将哈希槽指定给特定节点的ID。否则,哈希槽可以在两种特殊状态MIGRATING 和 IMPORTING。这两个特殊状态用于哈希槽从一个节点迁移到另一个。
 

  • 当一个槽被设置为MIGRATING,持有该哈希槽的节点仍会接受所有跟这个哈希槽有关的请求,但只有当查询的键还存在原节点时,原节点会处理该请求,否则这个查询会通过一个-ASK 重定向转发到迁移的目标节点。
  • 当一个槽被设置为IMPORTING,只有在接受到 ASKIN命令之后节点才会接受所有查询这个哈希槽的请求。如果客户端没有发送     ASKING命令,那么查询都会通过-MOVED 重定向错误转发到真正的哈希槽归属节点那里,这通常会发生。


这我们用实例让它哈希槽迁移更清晰些。假设我们有两个Redis master节点,称为A和B。我们想要把哈希槽8从节点A移到节点B,所以我们发送了这样的命令:
 

  • 我们向节点B发送:CLUSTER SETSLOT 8 IMPORTING A
  • 我们向节点A发送:CLUSTER SETSLOT 8 MIGRATING B


其他所有节点在每次请求的一个键是属于哈希槽8的时候,都会把客户端引向节点“A”。具体如下:
 

  • 所有关于已存在的键的查询都由节点“A”处理。
  • 所有关于不存在于节点A的键都由节点“B”处理,因为”A”将重定向客户端请求到“B”。


这种方式让我们可以不用在节点 A 中创建新的键。同时,一个叫做 redis-trib 的特殊脚本,用于重新分片和集群配置,把已存在的属于哈希槽 8的键从节点A移到节点B。这通过以下命令实现:

CLUSTER GETKEYSINSLOT slot count

上面这个命令会返回指定的哈希槽中 count 个键。对于每个返回的键,redis-trib 向节点 A 发送一个MIGRATE 命令,这样会以原子性的方式从A到B迁移指定的键(在移动键的过程中两个节点都被锁住,通常时间很短,所以不会出现竞争状况)。以下是 MIGRATE 的工作原理:

MIGRATE target_host target_port key target_database idtimeout

执行 MIGRATE 命令的节点会连接到目标节点,把序列化后的key发送过去,一旦收到OK回复就会从它自己的数据集中删除老的key。所以从一个外部客户端看来,在某个时间点,一个 key 要不就存在于节点A中要不就存在于节点B中。

在 Redis 集群中,不需要指定一个除了0号之外的数据库,但 MIGRATE 命令能用于其他跟 Redis 集群无关的的任务,所以它是一个通用的命令。MIGRATE 命令被优化了,使得即使在移动像长列表这样的复杂键仍然能做到快速。不过当在重配置一个拥有很多键且键的数据量都很大的集群的时候,如果使用它的应用程序来说就会有延时这个限制,这个过程就不是那么好了。
 

ASK 重定向

在前面的章节中,我们简短地提到了ASK重定向(ASK Redirection),为什么我们不能单纯地使用MOVED重定向呢?因为收到 MOVED,意味着我们认为哈希槽永久地归属到了另一个节点,并且接下来的所有请求都尝试发到目标节点上去。而 ASK 意味着我们只要下一个请求发送到目标节点上去。

这个命令是必要的,因为下一个关于哈希槽8的请求需要的键或许还在节点A中,所以我们希望客户端尝试在节点A中查找,如果需要的话然后在节点B中查找。 由于这是发生在 16384个槽的其中一个槽,所以对于集群的性能影响是在可接受的范围。

然而我们需要强制客户端的行为,以确保客户端会在尝试A中查找后去尝试在B中查找,如果客户端在发送查询前发送了ASKING命令,那么节点B只会接受被设为IMPORTING 的槽的查询。

基本上ASKING 命令在客户端设置了一个一次性标识(one-time flag),强制一个节点可以执行一次关于带有IMPORTING状态的槽的查询。

所以从客户端看来,ASK重定向的完整语义如下:
 

  • 如果接受到ASK重定向,发送单次请求重定向到目标节点,接着发送后续的请求到老的节点。
  • 先发送ASKING命令,再开始发送请求。
  • 现在不要更新本地客户端的映射表,把哈希槽8映射到B。


一旦完成了哈希槽8的转移,节点A会发送一个MOVED消息,客户端也许会永久地把哈希槽8映射到新的IP:端口号上。 注意,即使客户端出现bug,过早地执行这个映射更新,也是没有问题的,因为它不会在查询前发送ASKING命令,节点B会用MOVED重定向错误把客户端重定向到节点A上。
 

客户端首次连接和处理重定向

虽然可以有一个Redis的群集客户端不在内存中记住哈希槽配置(哈希槽与节点的映射),并只能通过联系随机节点等待被重定向,这样的客户端将是效率非常低。

Redis的集群客户应尽量足够聪明,记忆哈希槽配置。然而这种配置不必是最新的。因为联系错误的节点只会导致一个重定向,应当触发客户视图的更新。

客户通常需要获取哈希槽与节点映射的完整列表:
 

  • 启动时保存初始的哈希槽配置.
  • 当收到 MOVED重定向.


请注意,客户可能根据MOVED重定向更新变动的哈希槽,但是这通常不是有效的,因为通常配置中多个哈希槽一起修改(例如,如果一个slave升为 master,所有归属老master的哈希槽会重新映射)。更简单对MOVED重定向做出回应是,重新获取哈希槽节点映射表。

为了获取哈希槽配置Redis的群集提供了另一种不需要的解析的命令CLUSTER NODES,并只仅提供客户端严格需要的信息。

新的命令被称为CLUSTER SLOTS并槽提供了一组哈希槽范围,关联了主从节点服务于指定的哈希槽范围。

下面是CLUSTER插槽输出的例子:
 

127.0.0.1:7000> cluster slots
1) 1) (integer) 5461
2) (integer)10922
3) 1)"127.0.0.1"
2) (integer)7001
4) 1)"127.0.0.1"
2) (integer)7004
2) 1) (integer) 0
2) (integer)5460
3) 1)"127.0.0.1"
2) (integer)7000
4) 1)"127.0.0.1"
2) (integer) 7003
3) 1) (integer) 10923
2) (integer)16383
3) 1)"127.0.0.1"
2) (integer)7002
4) 1)"127.0.0.1"
2) (integer)7005



返回的数组中的每个元素的前两个子元素是该范围的始未哈希槽。附加元素表示地址端口对。第一个地址端口对是服务于哈希槽的master,和附加的地址端口对服务于相同槽slave,它不存在错误条件(即故障标志没有被设置)。
例如,输出的第一个元素表示,槽从5461至10922(开始和结束包括)由127.0.0.1:7001服务,并且可以通过127.0.0.1:7004水平扩展读负载。
CLUSTERSLOTS是不能保证返回覆盖整个16384插槽,如果群集配置不正确范围,所以客户初始化哈希槽配置时应当用NULL填充空节点,并报告一个错误,如果用户试图执行有关键的命令属于未分配的插槽。
当一个哈希槽被发现是未分配之前,返回一个错误给调用者之前,客户应该再次尝试读取哈希槽配置,检查群集现在配置是否正确。

多键操作

使用哈希标签,用户可以自由地使用多键操作。例如下面的操作是有效的:
MSET {user:1000}.name Angela {user:1000}.surname White
多键操作可能变得不可用,当键所属的哈希槽在进行重新分片。
更具体地,即使重新分片期间,多键操作目标键都存在并且处于相同节点(源或目的地节点)仍然可用。
在重新分片时,操作的键不存在或键在源节点和目的节点之间,将产生 -TRYAGAIN 错误。客户端可以一段时间后再尝试操作,或报错。
只要指定的哈希槽的迁移已经终止,所有多键操作可再次用于该散列槽。

通过slave节点水平扩展读

通常情况下从节点将客户端重定向到给定的命令哈希槽对应的master,但是客户端可以使用READONLY命令读来水平扩展读。
READONLY告诉客户端是允许读失效的数据并且不关心写请求。
当连接处于只读模式,当操作涉及到不是slave的主节点提供服务的键,集群将发送一个重定向到客户端。这可能发生的原因是:

  • 客户端发送一个命令对应的哈希槽不是由这个slave的master服务。
  • 集群重新配置(例如重新分片 )并且slave不再能够服务于给定的哈希槽命令

当发生这种情况如前面部分中说明的,客户端应该更新其哈希槽映射表。
连接的只读状态可以使用READWRITE命令清除。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值