Redis 集群

本文大部分摘自《Redis入门指南 第2版》李子骅 编著,使用的 Redis版本是5.0.3,如有侵权,请联系删除!


即使使用哨兵,此时的Redis集群的每个数据库依然存有集群中的所有数据,从而导致集群的总数据存储量受限于可用存储内存最小的数据库节点,形成木桶效应。

对Redis进行水平扩容,在旧版Redis中通常使用客户端分片来解决这个问题,即启动多个Redis数据库节点,由客户端决定每个键交由哪个数据库节点存储,下次客户端读取该建时直接到该节点读取。这样可以实现将整个数据分布存储在N个数据库节点中,每个节点只存储总数据的1/N。单对于需要扩容的场景来说,在客户端分片后,如果想增加更多的节点,就需要对数据进行手工迁移,同时在迁移过程中为了保证数据的一致性,还需要将集群暂时下线,相对比较复杂。

考虑到Redis实例比较轻量的特点,可以采用预分片(presharding)技术在一定程度上避免此问题。具体来说就是在节点部署初期,就提前考虑日后的存储规模,建立足够多的实例。初期数据很少,每个节点存储的数据也很少,但由于节点轻量的特性,数据之外内存开销并不大,这使得很少的服务器就可以运行这些实例。日后存储规模扩大后,所要做的不过是将某些实例迁移到其他服务器上,而不需要对所有数据进行重新分片并进行集群下线和数据迁移了。

无论如何,客户端分片终归是有非常多的缺点,比如维护成本高,增加、删除节点繁琐等。Redis从3.0版本开始支持集群(Cluster)。集群的特点在于拥有和单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持。另外集群支持几乎所有的单机实例支持的命令,对于涉及多键(如MGET)的命令,如果每个键都位于同一个节点中,则可以正常支持,否则会提示错误。除此之外集群还有一个限制就是只能使用默认的0号数据库,如果执行SELECT切换数据库则会提示错误。

哨兵与集群是两个独立的功能,但从特性来看哨兵可以视作集群的子集,当不需要数据分片或已经在客户端进行分片的场景下哨兵就足够使用了,但如果需要进行水平扩容,则集群是一个非常好的选择。

一、集群配置

使用集群,只需要将每个数据库节点的cluster-enabled配置选项打开即可。每个集群至少需要3个主数据库才能运行。为了演示集群的应用场景及故障恢复等操作,这里以配置一个3主3从的集群为例(集群都应该使用奇数个节点,少数服从多数)。

限于条件,我这里在一台机器上演示。首先准备6个配置文件。将Redis源码中的redis.conf配置文件模板复制6份到${REDIS_HOME}/conf目录下,分别命名如下:

-rw-r--r-- 1 root root 62157 3月  20 14:26 6380.conf
-rw-r--r-- 1 root root 62156 3月  20 14:29 6381.conf
-rw-r--r-- 1 root root 62156 3月  20 14:35 6382.conf
-rw-r--r-- 1 root root 62156 3月  20 14:36 6383.conf
-rw-r--r-- 1 root root 62156 3月  20 14:36 6384.conf
-rw-r--r-- 1 root root 62156 3月  20 14:37 6385.conf

分别修改每个配置文件的端口为6380、6381....,并且打开cluster-enabled yes。

集群会将当前节点记录的集群状态持久化到指定文件中,这个文件默认为当前工作目录下的nodes.conf文件。每个节点对应的文件必须不同,否则会造成启动失败(当然,如果在节点分布在不同的机器上不存在),所以启动节点时要注意最后为每个节点使用不同的工作目录(dir参数指定的目录),或者通过clutser-config-file选项修改持久化文件的名称。例如,修改6380.conf的cluster-config-file为如下:

cluster-config-file nodes-6380.conf

其他类似。

依次启动6个Redis实例,启动之后,每个节点都会输出类似下面的内容(可以在配置文件的logfile ""配置日志路径):

Node configuration loaded, I'm 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad

其中7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad表示该节点运行的id,运行ID是节点在集群中的唯一标识。

启动后,可以使用Redis客户端连接任意一个节点,使用info命令来查看集群是否正常启用了:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-cli -p 6380
127.0.0.1:6380>
127.0.0.1:6380>
127.0.0.1:6380> info cluster
# Cluster
cluster_enabled:1

其中,cluster_enabled为1表示集群正常启用了。现在每个节点都是完全独立的,要将它们加入同一个集群里还需要几个步骤。

在Redis 5.0之前,需要使用Redis提供的一个辅助工具redis-trib.rb完成这一任务,因为redis-trib.rb使用Ruby语言编写的,所以运行前需要在服务器上安装Ruby程序。Ubuntu系统执行如下命令安装:

root@wuychn: apt-get install ruby
root@wuychn: gem install redis

CentOS系统执行如下命令安装:

yum install ruby
yum install rubygems
gem install redis

当执行gem install redis时,可能会报错,提示 redis requires Ruby version >= 2.3.0.,参考https://www.cnblogs.com/blueskyli/p/9084548.html解决。

使用redis-trib.rb初始化集群,只需执行:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-trib.rb create --replicas 1 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385

其中create参数表示要初始化集群,--replicas 1表示每个主数据库拥有的从数据库个数为1,所以整个集群共有3(6/2)个主数据库及3个从数据库。

在Redis 5.0之后,Redis使用redis-cli作为创建集群的命令,使用C语言实现,不再使用Ruby语言。所以,在Redis 5.0之后的版本中,将节点加入集群需要执行如下命令(这里需要注意,最好不要使用127.0.0.1,否则当客户端连接的时候,会报Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: /127.0.0.1:6381这样的错误,应该使用192.168.x.x这样的ip):

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-cli --cluster create 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6383 to 127.0.0.1:6380
Adding replica 127.0.0.1:6384 to 127.0.0.1:6381
Adding replica 127.0.0.1:6385 to 127.0.0.1:6382
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
M: 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381
   slots:[5461-10922] (5462 slots) master
M: 2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382
   slots:[10923-16383] (5461 slots) master
S: 1464de78e01f14b871d49ce0d91a06a7524a735c 127.0.0.1:6383
   replicates 5bcc39e7597da852734a23e4b6c88c1f547a4ee7
S: 93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384
   replicates 2092a2d621572bc5176cce0cb23156053e4fd108
S: cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385
   replicates 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
......
>>> Performing Cluster Check (using node 127.0.0.1:6380)
M: 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 2092a2d621572bc5176cce0cb23156053e4fd108
S: cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385
   slots: (0 slots) slave
   replicates 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad
S: 1464de78e01f14b871d49ce0d91a06a7524a735c 127.0.0.1:6383
   slots: (0 slots) slave
   replicates 5bcc39e7597da852734a23e4b6c88c1f547a4ee7
M: 2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

加入集群的过程如下:首先尝试连接所有节点,并发送PING命令确定节点能够正常服务,如果有任何节点无法连接,则创建失败。同时发送INFO命令获取每个节点的运行ID以及是否开启了集群功能。准备就绪后集群会向,每个节点发送CLUSTER MEET命令,格式为CLUSTER MEET ip port,用来告诉当前节点指定ip和port上运行的节点也是集群的一部分,从而使得所有节点最终归入一个集群。之后配置主从节点,分配的原则是进来保证每个主数据库运行在不同的ip上,同时保证每个从数据库和主数据库均不允许在同一ip上,以保证系统的容灾能力。可以从上面的输出信息看到注册的分配结果,由于我这里所有节点都在同一个ip上,所以输出了一个警告信息。分配完成之后,会为每个主数据库分配插槽,这个过程其实就是分配哪些键归哪些节点负责。之后对每个要成为从数据库的节点发送CLUSTER REPLICATE 主数据库的运行ID 来将当期节点转换为从数据库并复制指定ID节点的数据。

此时集群即创建完成,使用Redis命令行客户端连接任意一个节点执行CLUSTER NODES可以获取集群中的所有节点信息。例如,在6380上执行:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-cli -p 6380
127.0.0.1:6380>
127.0.0.1:6380> cluster nodes
5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381@16381 master - 0 1553159248000 2 connected 5461-10922
93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384@16384 slave 2092a2d621572bc5176cce0cb23156053e4fd108 0 1553159250513 5 connected
cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385@16385 slave 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 0 1553159250000 6 connected
1464de78e01f14b871d49ce0d91a06a7524a735c 127.0.0.1:6383@16383 slave 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 0 1553159251520 4 connected
2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382@16382 master - 0 1553159248500 3 connected 10923-16383
7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380@16380 myself,master - 0 1553159248000 1 connected 0-5460

二、节点添加

Redis是使用cluster meet命令来使每个节点认识集群中的其他节点的,可想而知如果要向及群中加入其他节点,也需要使用cluster meet命令实现。加入新节点只需要向新节点发送如下命令即可:

cluster meet ip port

ip和port是集群中任意一个节点的地址和端口号。新节点(记做A)收到cluster meet命令后,会与该地址和端口号的节点(记做B)进行握手,当B与A握手成功后,B会使用Gossip协议将A节点的信息通知给集群中的每一个节点。通过这一方式,即使集群中有多个节点,也只需要选择meet其中一个节点,即可使新节点最终加入到集群中。

例如,再复制一份配置文件,设置端口为6386,启动该实例:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-server ../conf/6386.conf
root@wuychn:/opt/apps/redis-5.0.3/src#

之后进入到命令行,执行cluster meet:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-cli -p 6386
127.0.0.1:6386>
127.0.0.1:6386> cluster meet 127.0.0.1 6380
OK
127.0.0.1:6386>

在任意节点再次查看集群的节点信息:

127.0.0.1:6380> cluster nodes
5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381@16381 master - 0 1553160275000 2 connected 5461-10922
93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384@16384 slave 2092a2d621572bc5176cce0cb23156053e4fd108 0 1553160274014 5 connected
cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385@16385 slave 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 0 1553160274000 6 connected
1464de78e01f14b871d49ce0d91a06a7524a735c 127.0.0.1:6383@16383 slave 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 0 1553160277037 4 connected
2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382@16382 master - 0 1553160276030 3 connected 10923-16383
7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380@16380 myself,master - 0 1553160271000 1 connected 0-5460
0b6f43e89bf5d4300efd7c38f0597093f9782479 127.0.0.1:6386@16386 master - 0 1553160275022 0 connected
127.0.0.1:6380>

可见6386这个节点已经成功加入到了集群,并且是主节点。

三、插槽分配

新的节点加入集群后有两种选择:要么使用CLUSTER REPLICATE命令复制每个主数据库来以从数据库的形式运行,要么向集群申请插槽(slot)来以主数据库的形式运行。

在一个集群中,所有的键会被分配给16384个插槽,而每个主数据库会负责处理其中的一部分插槽。回过头来看看差距集群时的输出:

M: 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
M: 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381
   slots:[5461-10922] (5462 slots) master
M: 2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382
   slots:[10923-16383] (5461 slots) master

上面的每一行表示一个主数据库的信息,可以看到6380负责处理0到5460这5461个插槽,依次类推。虽然在初始化集群时分配给每个节点的插槽都是连续的,但是实际上Redis并没有做此限制,可以将任意的几个插槽分配给任意节点负责。

Redis将每个键的键名的有效部分使用CRC16算法计算出散列值,然后取对16384的余数,这样可以使得每个键都可以分配到16384个插槽中,从而分配给指定一个节点处理。这里有效键名是指:

(1)、如果键名包含{符号,并且在{符号之后存在}符号,并且{和}之间至少有一个字符,则有效部分是指{和}之间的内容。

(2)、如果不满足上一条,那么整个键名为有效部分。

插槽的分配分为以下几种情况:

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

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

第一种情况使用CLUSTER ADDSLOTS命令来实现,该命令的用法为:

CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]

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

127.0.0.1:6380> CLUSTER ADDSLOTS 100 101

如果指定插槽已经分配过了,则会提示:

(error) ERR Slot 100 is already busy

可以使用命令CLUSTER SLOTS查看插槽的分配情况:

127.0.0.1:6380> CLUSTER SLOTS
1) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6381
      3) "5bcc39e7597da852734a23e4b6c88c1f547a4ee7"
   4) 1) "127.0.0.1"
      2) (integer) 6383
      3) "1464de78e01f14b871d49ce0d91a06a7524a735c"
2) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 6382
      3) "2092a2d621572bc5176cce0cb23156053e4fd108"
   4) 1) "127.0.0.1"
      2) (integer) 6384
      3) "93098e8b5dd63274ed262f31ff8c1df26dd92c38"
3) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 6380
      3) "7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad"
   4) 1) "127.0.0.1"
      2) (integer) 6385
      3) "cf942568968bce50d5d0857f7204fbfe0c7082c9"

对于情况二,在Redis 5.0之前的版本,可以使用redis-trib.rb来对插槽进行迁移,使用方法如下:

./redis-trib.rb reshard 127.0.0.1:6380

在Redis 5.0之后的版本,使用redis-cli对插槽进行迁移:

root@wuychn:/opt/apps/redis-5.0.3/src# redis-cli --cluster reshard 127.0.0.1:6380
>>> Performing Cluster Check (using node 127.0.0.1:6380)
M: 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 2092a2d621572bc5176cce0cb23156053e4fd108
S: cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385
   slots: (0 slots) slave
   replicates 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad
S: 1464de78e01f14b871d49ce0d91a06a7524a735c 127.0.0.1:6383
   slots: (0 slots) slave
   replicates 5bcc39e7597da852734a23e4b6c88c1f547a4ee7
M: 2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: 0b6f43e89bf5d4300efd7c38f0597093f9782479 127.0.0.1:6386
   slots: (0 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 1
What is the receiving node ID? 5bcc39e7597da852734a23e4b6c88c1f547a4ee7
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1: all

Ready to move 1 slots.
  Source nodes:
    M: 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380
       slots:[0-5460] (5461 slots) master
       1 additional replica(s)
    M: 2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
    M: 0b6f43e89bf5d4300efd7c38f0597093f9782479 127.0.0.1:6386
       slots: (0 slots) master
  Destination node:
    M: 5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381
       slots:[5461-10922] (5462 slots) master
       1 additional replica(s)
  Resharding plan:
    Moving slot 0 from 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 0 from 127.0.0.1:6380 to 127.0.0.1:6381:

如上所示,执行相应命令后,首先会询问想要迁移多少个插槽(How many slots do you want to move (from 1 to 16384)?),我们只需要迁移一个,所以输入1即可。接下来询问把插槽迁移到哪个节点(What is the receiving node ID?),我这里把插槽迁移到6381节点,所以输入6381的id。之后询问从哪一个节点移出插槽,可以输入all代表所有节点(如果输入具体某个节点的ID,则还需要输入done确认)。最后输入yes确认分片方案,重新分片即成功。使用CLUSTER SLOTS命令获取当前插槽的分配情况:

127.0.0.1:6380> CLUSTER SLOTS
1) 1) (integer) 0
   2) (integer) 0
   3) 1) "127.0.0.1"
      2) (integer) 6381
      3) "5bcc39e7597da852734a23e4b6c88c1f547a4ee7"
   4) 1) "127.0.0.1"
      2) (integer) 6383
      3) "1464de78e01f14b871d49ce0d91a06a7524a735c"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6381
      3) "5bcc39e7597da852734a23e4b6c88c1f547a4ee7"
   4) 1) "127.0.0.1"
      2) (integer) 6383
      3) "1464de78e01f14b871d49ce0d91a06a7524a735c"
3) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 6382
      3) "2092a2d621572bc5176cce0cb23156053e4fd108"
   4) 1) "127.0.0.1"
      2) (integer) 6384
      3) "93098e8b5dd63274ed262f31ff8c1df26dd92c38"
4) 1) (integer) 1
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 6380
      3) "7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad"
   4) 1) "127.0.0.1"
      2) (integer) 6385
      3) "cf942568968bce50d5d0857f7204fbfe0c7082c9"

可以看到第0号插槽已由6381节点负责。

除了使用工具,还可以手工完成插槽迁移。

四、获取与插槽对应的节点

对于指定的键,可以通过前文的算法来计算出其属于哪一个插槽,但是如何获取某一个键由哪一个节点负责呢?

当客户端向集群中的任意一个节点发送命令后,该节点会判断相应的键是否在当前节点中,如果在则会像单机示例一样正常处理该命令;如果键不在该节点中,就会返回一个MOVE重定向请求,告诉客户端这个键目前由哪个节点负责,然后客户端再将同样的请求向目标节点重新发送一次以获得结果。

例如,键foo实际该由6382节点负责,如果尝试在6380节点执行与键foo相关的命令,就会有如下输出:

127.0.0.1:6380> set foo bar
(error) MOVED 12182 127.0.0.1:6382

返回的是一个MOVE重定向请求,12182表示foo所属的插槽号,127.0.0.1:6382是负责该插槽的节点的ip和端口,客户端收到重定向请求后,应该将命令重新向6382节点发送一次:

127.0.0.1:6382> set foo bar
OK

Redis命令行客户端提供了集群模式来支持自动重定向,使用-c参数启用:

root@wuychn:/opt/apps/redis-5.0.3/src# ./redis-cli -c -p 6380
127.0.0.1:6380>
127.0.0.1:6380> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:6382
OK

可见加入-c后,如果当前节点并不负责要处理的键,Redis命令行客户端会进行自动重定向。

五、故障恢复

在一个集群中,每个节点都会定期向其他节点发送PING命令,并通过有没有收到回复来判断目标节点是否已经下线。如果一定时间内目标节点没有回复,则发起PING命令的节点会认为目标节点疑似下线(PFAIL)。疑似下线可与哨兵的主观下线类比,两者都表示某一节点从自身角度认为目标节点是下线的状态。与哨兵模式类似,如果要是在整个集群中的所有节点都认为某一节点已经下线,需要一定数量的节点都认为该节点疑似下线才可以。这一过程为:

(1)、一旦节点A认为节点B是疑似下线状态,就会在集群中传播该消息,所有其他节点收到该消息后会记录下这一信息;

(2)、当集群中的某一节点C收集到半数以上的节点认为B是疑似下线状态时,就会将B标记为下线(FAIL),并向集群中的其他节点传播该消息,从而使得B在整个集群中下线。

在集群中,当一个主数据库下线时,就会导致一部分插槽无法写入。这时如果该主数据库至少拥有一个从数据库,集群就会进行故障恢复操作来将其中一个从数据库变为主数据库。选择哪个从数据库作为主数据库与哨兵中选择领头哨兵的过程一样,都是基于Raft算法:

(1)、发现其复制的主数据库下线的从数据库(A)向每个集群中的节点发送请求,要求对方选自己成为主数据库。

(2)、如果收到请求的节点没有选择其他人,则同意将A设置为主数据库。

(3)、如果A发现有超过集群中节点总数一半的节点同意选自己成为主数据库,则A成功成为主数据库。

(4)、当有多个从数据库同时参选主数据库,则会出现没有任何节点当选的可能,此时每个参选节点将等待一个随机时间重新发起参选请求,进行下一轮的选举,直到选举成功。

当某一个数据库当选为主数据库时,会通过命令SLAVEOF NO ONE将自己转换为主数据库,并将旧的主数据库的插槽转换给自己负责。

如果一个至少负责一个插槽的主数据库下线且没有相应的从数据库可以进行故障恢复,则整个集群会默认进入下线状态无法继续工作。如果想在这种情况下集群仍能正常工作,可以修改配置cluster-require-full-coverage为no(默认为yes)。

六、节点删除

如果节点是从节点的,在Redis 5.0 之前的版本,直接使用下面的命令删除即可:

./redis-trib.rb del-node ip:port 节点的ID

其中节点ID可以通过CLUSTER NODES命令查看。

在Redis 5.0之后的版本,使用如下命令删除:

root@wuychn:/opt/apps/redis-5.0.3/src# redis-cli --cluster del-node 127.0.0.1:6383 1464de78e01f14b871d49ce0d91a06a7524a735c
>>> Removing node 1464de78e01f14b871d49ce0d91a06a7524a735c from cluster 127.0.0.1:6383
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

删除之后,在任意节点使用CLUSTER NODES查看,被删除的节点已经不在集群中了:

127.0.0.1:6382> CLUSTER NODES
cf942568968bce50d5d0857f7204fbfe0c7082c9 127.0.0.1:6385@16385 slave 7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 0 1553241562024 6 connected
7ce0b2e3d0a3f5dc74d28ddb3ae6d7b510ee15ad 127.0.0.1:6380@16380 master - 0 1553241565044 1 connected 1-5460
2092a2d621572bc5176cce0cb23156053e4fd108 127.0.0.1:6382@16382 myself,master - 0 1553241561000 3 connected 10923-16383
5bcc39e7597da852734a23e4b6c88c1f547a4ee7 127.0.0.1:6381@16381 master - 0 1553241563031 7 connected 0 5461-10922
0b6f43e89bf5d4300efd7c38f0597093f9782479 127.0.0.1:6386@16386 master - 0 1553241563000 0 connected
93098e8b5dd63274ed262f31ff8c1df26dd92c38 127.0.0.1:6384@16384 slave 2092a2d621572bc5176cce0cb23156053e4fd108 0 1553241564037 5 connected

如果节点是主节点,首先需要把节点中的哈希槽迁移到其他节点中,迁移节点的命令参考前文,之后执行删除节点的命令即可。

如果想重新搭建集群,则可以先停掉所有节点,然后删除所有的cluster-config-file指定的文件(配置文件中配置),再次启动并搭建集群即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值