• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Redis Cluster增加删除节点(二)

Redis 彭东稳 8年前 (2017-03-17) 25881次浏览 已收录 0个评论

一、Redis Cluster增加删除节点

Redis cluster技术应用实践(一)

前面介绍了redis-trib.rb是使用CLUSTER MEET命令来使每个节点认识集群中的其他节点的,可想而知如果想要向集群中添加新的节点,也需要使用CLUSTER MEET命令来实现。加入新节点非常简单,只需要向新节点发送如下命令即可:

此操作不会影响当前集群。ip和port是集群中任意一个节点的地址和端口号,A接收到客户端发来的命令后,会与该地址和端口号的节点B进行握手,使B将A认作当前集群中的一员。当B与A握手成功后,B会使用Gossip协议将节点A的信息通知给集群中的每一个节点。通过这一方式,即使集群中有多个节点,也只需要选择MEET其中任意一个节点,即可使新节点最终加入整个集群中。

集群增加节点和删除节点相关命令:

二、插槽的分配

新的节点加入集群后有两种选择:

1)要么使用CLUSTER REPLICATE命令复制每个主数据库来以数据库的形式运行。

2)要么向集群申请分配插槽来以主数据库的形式运行。

在一个集群中,所有的键会被分配给16384个插槽,而每个主数据库会负责处理其中的一部分插槽。默认使用redis-trib.rb初始化集群时分配给每个节点的插槽都是连续的,但是实际上Redis并没有此限制,可以将任意的几个插槽分配给任意的节点负责。

在介绍如何将插槽分配给指定的节点前,先来介绍键与插槽的对应关系。Redis将每个键的键名的有效部分使用CRC16算法计算出散列值,然后取对16384的余数。这样使得每个键都可以分配到16384个插槽中,进而分配的指定的一个节点中处理。这里键名的有效部分是指,如果键名包含{}号,则{}号内的字符则视为有效值。例如键hello.world的有效部分为”hello.world”,键{user102}有效部分为”user102″。如果命令设计多个键(如MGET),只有当所有键都位于同一个节点时Redis才能正常支持。利用键的分配规则,可以将所有相关的键的有效部分设置成同样的值使得相关键都能分配到同一个节点以支持多键操作。比如{user102}.first和{user102}.last会被分配到同一个节点,所以可以使用MGET {user102}.first {user102}.last来同时获取两个键的值。如下操作:

介绍完键与插槽的对应关系后,接下来再来介绍如何将插槽分配给指定节点。插槽的分配分为如下几种情况。

1)插槽之前没有被分配过,现在想分配给指定节点。

2)插槽之前被分配过,现在想移动到指定节点。

其中第一种情况使用CLUSTER ADD SLOT S命令来实现,redis-trib.rb也是通过该命令在创建集群时为新节点分配插槽的。CLUSTER ADDSLOTS命令的用法为:

如想将100和101两个插槽分配给某个节点,只需要在该节点执行:

即可,如果指定插槽已经分配过了,则会提示:(error) ERR Slot 100 is already busy。可以通过CLUSTER SLOTS来查看插槽的分配情况,如:

其中返回的结果的格式很容易理解,一共2条记录,每条记录的前两个值表示插槽的开始号和结束号码,后面的值则为负责该插槽的主从节点,主数据库始终在第一位。

对于第二种情况处理起来就相对复杂一些,不过redis-trib.rb提供了比较方便的方式来对插槽进行迁移。我们首先使用redis-trib.rb将1000个插槽从6551/6552/6553迁移到6557。

我们先往集群中添加2000个key。

然后添加2个节点(一主一从),开启6557和6558两个节点。

把这两个节点添加到集群中。

开始迁移插槽。

其中reshard表示告诉redis-trib.rb要重新分片,10.99.73.11:6551是集群中的任意一个节点的地址和端口,redis-trib.rb会自动获取集群信息。接下来,redis-trib.rb将会询问具体如何进行重新分片,首先会询问想要迁移多少个插槽:

我需要迁移1000个,所以输入1000后回车。接下来redis-trib.rb会询问要把插槽迁移到哪个节点:

可以通过CLUSTER NODES命令获取6557的运行ID,输入并回车。接着最后一步是询问从哪个节点移出插槽:

这里可以输入指定的节点,如下:

还可以输入all表示全部节点重新洗牌,这里我们输入all回车,会让你确认一下是否重新分配,输入yes即可开始迁移slot操作。如果数据量大的话,这个操作时间还是蛮久的。

这里还可以使用参数的方式进行slot迁移,具体可以看Redis cluster管理工具redis-trib.rb详解(三)

当迁移完成后,可以看一下当前的集群节点状态:

从集群节点状态可以看出,当我们重新洗牌进行迁移时,会从每个主节点迁移一些slot到新节点上,反正看上去乱乱的。

然后就可以把6558提升为6557的slave。

验证一下迁移后的数据。

数据安全迁移完成。

那么redis-trib.rb实现重新分片的原理是什么呢?我们如何不借助redis-trib.rb手工进行重新分片呢?使用如下命令即可:

如想要把6557的0号slot迁移回6551(如果slot中有key则迁移会报错):

此时重新使用cluster slots查看插槽的分配情况,可以看到已经恢复如初了。然而这样迁移插槽的前提时插槽中并没有任何键(如果有key则迁移会报错),因为使用CLUSTER SETSLOT命令迁移插槽时并不会连同相应的键一起迁移,这就造成了客户端在指定节点无法找到未迁移的键,造成这些键对客户端来说”丢失了”。为此需要手工获取插槽中存在哪些键,然后将每个键迁移到新的节点才行。

手工获取某个插槽存在哪些键的方法是:

之后对每个键,使用MIGRATE命令将其迁移到目标节点:

其中COPY选项表示不将键从当前数据库中删除,而是复制一份副本。REPLACE表示如果目标节点存在同名键,则覆盖。因为集群模式只能使用0号数据库,所以数据库号码始终未0.如要把键pkey414从当前节点(如6557)迁移到6551:

至此,我们已经知道如果将插槽委派给其他节点,并同时将当前节点中插槽下所有的键迁移到目标节点中。然而还有最后一个问题是如果要迁移的数据量比较大,整个过程会花费较长时间,那么究竟在什么时候执行CLUSTER SETSLOT命令来完成插槽的交接呢?如果在键迁移未完成时执行,那么客户端就会尝试在新的节点读取键值,此时还没有迁移完成,自然有可能读不到键值,从而造成相关键的临时”丢失”。相反,如果在键迁移完成后再执行,那么在迁移时客户端会在旧的节点读取键值,然后有些键已经迁移到新的节点上了,同样也会造成键的临时”丢失”。那么redis-trib.rb工具是如何解决这个问题的呢?

Redis提供了如下两个命令用来实现在集群不下线的情况下迁移数据?

进行迁移时,假设要把0号插槽从A迁移到B,此时redis-trib.rb会依次执行如下操作:

1)在A(源)执行CLUSTER SETSLOT 0 MIGRATING B。

2)在B(目标)执行CLUSTER SETSLOT 0 IMPORTING A。

3)执行CLUSTER GETKEYSINSLOT 0获取0号插槽的键列表。

4)执行CLUSTER SETSLOT 0 NODE B来完成迁移(A和B都执行)。

从上面的步骤来看redis-trib.rb多了1和2两个步骤,这两个步骤就是为了解决迁移过程中键的临时”丢失”问题。首先执行完前两步后,当客户端向A请求插槽0中的键时,如果键存在(即尚未迁移),则正常处理,如果不存在,则返回一个ASK跳转请求,告诉客户端这个键在B里。客户端接收到ASK跳转请求后,首先向B发送ASKING命令,然后再重新发送之前的命令。相反,当客户端向B请求插槽0中的键时,如果前面执行了ASKING命令,则返回键值内容,否则返回MOVED跳转请求,这样一来客户端只要能够处理ASK跳转,则可以在数据库迁移时自动从正确的节点获取到对应的键值,避免了键在迁移过程中临时”丢失”的问题。

三、Redis Cluster手动迁移SLOT示例

下面模拟迁移一个SLOT到指定节点,可以看看redis-trib.rb都做了哪些工作。另外需要注意观察源节点和目标节点交叉执行的指令。

源节点(10.100.173.172:6380)

目标节点(10.100.173.170:6380)

源节点(10.100.173.172:6380)

目标节点(10.100.173.170:6380)

源节点(10.100.173.172:6380)

目标节点(10.100.173.170:6380) 

一个SLOT手动迁移的过程就结束了。

更多管理方式请看:Redis cluster管理工具redis-trib.rb详解(四)


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (3)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!