list操作 rediscluster_redis集群常见问题

启动节点

Redis服务器在启动时,会根据cluster-enabled配置决定是否开启服务器的集群模式。如果未开启,就进入stand alone模式,以普通单机Redis方式运行。否则进入集群模式。

一个Redis集群由多个节点(node)组成。初始化时,每个node都是相互独立的,它们都处于一个只包含自己的集群当中。要想组成一个真正可用的集群,必须将多个独立的节点连接起来。

向一个node发送CLUSTER MEET命令,可以让node与指定的节点进行握手,握手成功后,指定的节点就加入到node所在的集群中。

新的节点加入集群后。node会通过Gossip协议传播给集群中的其他节点,让其他节点也与新加入的节点握手。最终经过一段时间后,集群中的所有节点就建立起了连接。

实现数据分区

槽(slot)概念:

Redis Cluster中有一个16384长度的虚拟的槽的概念,他们并不是真正存在的,他们的编号为0、1、2、3……16382、16383。Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权;集群使用公式CRC16(key) % 16384来计算键key属于哪个槽,其中CRC16(key)语句用于计算键key的CRC16校验和。

节点的槽指派信息:

clusterNode结构的slots属性和numslot属性记录了节点负责处理那些槽

struct clusterNode {

unsignedchar slots[16384/8];  // 二进制位数组(bit array),这个数组的长度为16384/8=2048个字节,共包含16384个二进制位

int numslots;//节点负责处理的槽的数量,即是slots数组中值为1的二进制位的数量

};

Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。时间复杂度为O(1)。

传播节点的槽指派信息(struct clusterNode)

一个节点会将自己的slots数组通过消息发送给集群中的其他节点,告知其他节点自己 目前负责处理哪些槽

客户端的api,可以对指定的数据,让他们走同一个hash slot,通过hash tag来实现

redis cluster哈希槽数量不能改变,因为代码算法写死了,固定是2的14次方这个数字上16384

摘自Redis官网的Data type章节,内存允许的情况下,一个hash slot可以存超过40亿数据

一个hash slot中会有很多key和value。你可以理解成表的分区。使用单节点时的redis时只有一个表,所有的key都放在这个表里;2.改用Redis Cluster以后会自动为你生成16384个分区表。

集群节点属性

集群中每个Master node负责存储数据、集群状态,包括slots与nodes对应关系。Master nodes能够自动发现其他nodes,检测failure节点,当某个Master节点失效时,集群能将核实的Slave提升为Master。节点定时会将这些信息发送给其他节点,cluster nodes命令输出是空格分割的CSV字符串,每行代表集群中的一个节点

67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002 master - 0 1426238316232 2 connected 5461-10922

每行的组成结构如下:

...

每项的含义如下:

id: 节点ID,是一个40字节的随机字符串,这个值在节点启动的时候创建,并且永远不会改变(除非使用CLUSTER RESET HARD命令)。

ip:port: 客户端与节点通信使用的地址.

flags: 逗号分割的标记位,可能的值有: myself, master, slave, fail?, fail, handshake, noaddr, noflags. 下一部分将详细介绍这些标记.

master: 如果节点是slave,并且已知master节点,则这里列出master节点ID,否则的话这里列出”-“。

ping-sent: 最近一次发送ping的时间,这个时间是一个unix毫秒时间戳,0代表没有发送过.

pong-recv: 最近一次收到pong的时间,使用unix时间戳表示.

config-epoch: 节点的epoch值(or of the current master if the node is a slave)。每当节点发生失败切换时,都会创建一个新的,独特的,递增的epoch。如果多个节点竞争同一个哈希槽时,epoch值更高的节点会抢夺到。

link-state: node-to-node集群总线使用的链接的状态,我们使用这个链接与集群中其他节点进行通信.值可以是 connected 和 disconnected.

slot: 哈希槽值或者一个哈希槽范围. 从第9个参数开始,后面最多可能有16384个 数(limit never reached)。代表当前节点可以提供服务的所有哈希槽值。如果只是一个值,那就是只有一个槽会被使用。如果是一个范围,这个值表示为起始槽-结束槽,节点将处理包括起始槽和结束槽在内的所有哈希槽。

各flags的含义 (上面所说数据项3):

myself: 当前连接的节点.

master: 节点是master.

slave: 节点是slave.

fail?: 节点处于PFAIL 状态。 当前节点无法联系,但逻辑上是可达的 (非 FAIL 状态).

fail: 节点处于FAIL 状态. 大部分(一半以上)节点都无法与其取得联系将会将改节点由 PFAIL 状态升级至FAIL状态。

handshake: 还未取得信任的节点,当前正在与其进行握手.

noaddr: 没有地址的节点(No address known for this node).

noflags: 连个标记都没有(No flags at all)

纪元(epoch)概念作用

用来给事件增加版本号。Redis 集群中的纪元主要是两种:currentEpoch 和 configEpoch。

currentEpoch

这是一个集群状态相关的概念,可以当做记录集群状态变更的递增版本号。每个集群节点,都会通过 server.cluster->currentEpoch 记录当前的 currentEpoch,集群节点创建时,不管是 master 还是 slave,都置 currentEpoch 为 0,当前节点接收到来自其他节点的包时,如果发送者的 currentEpoch(消息头部会包含发送者的 currentEpoch)大于当前节点的currentEpoch,那么当前节点会更新 currentEpoch 为发送者的 currentEpoch。因此,集群中所有节点的 currentEpoch 最终会达成一致,相当于对集群状态的认知达成了一致。

currentEpoch 作用

当集群的状态发生改变,某个节点为了执行一些动作需要寻求其他节点的同意时,就会增加 currentEpoch 的值。目前 currentEpoch 只用于 slave 的故障转移流程,当 slave A 发现其所属的 master 下线时,就会试图发起故障转移流程。首先就是增加 currentEpoch 的值,这个增加后的 currentEpoch 是所有集群节点中最大的。然后slave A 向所有节点发起拉票请求,请求其他 master 投票给自己,使自己能成为新的 master。其他节点收到包后,发现发送者的 currentEpoch 比自己的 currentEpoch 大,就会更新自己的 currentEpoch,并在尚未投票的情况下,投票给 slave A,表示同意使其成为新的 master。

configEpoch

这是一个集群节点配置相关的概念,每个集群节点都有自己独一无二的 configepoch。所谓的节点配置,实际上是指节点所负责的槽位信息。

每一个 master 在向其他节点发送包时,都会附带其 configEpoch 信息,以及一份表示它所负责的 slots 信息。而 slave 向其他节点发送包时,其包中的 configEpoch 和负责槽位信息,是其 master 的 configEpoch 和负责的 slot 信息。节点收到包之后,就会根据包中的 configEpoch 和负责的 slots 信息,记录到相应节点属性中。

configEpoch 作用

configEpoch 主要用于解决不同的节点的配置发生冲突的情况。举个例子就明白了:节点A 宣称负责 slot 1,其向外发送的包中,包含了自己的 configEpoch 和负责的 slots 信息。节点 C 收到 A 发来的包后,发现自己当前没有记录 slot 1 的负责节点(也就是 server.cluster->slots[1] 为 NULL),就会将 A 置为 slot 1 的负责节点(server.cluster->slots[1] = A),并记录节点 A 的 configEpoch。后来,节点 C 又收到了 B 发来的包,它也宣称负责 slot 1,此时,如何判断 slot 1 到底由谁负责呢?

这就是 configEpoch 起作用的时候了,C 在 B 发来的包中,发现它的 configEpoch,要比 A 的大,说明 B 是更新的配置。因此,就将 slot 1 的负责节点设置为 B(server.cluster->slots[1] = B)。在 slave 发起选举,获得足够多的选票之后,成功当选时,也就是 slave 试图替代其已经下线的旧 master,成为新的 master 时,会增加它自己的 configEpoch,使其成为当前所有集群节点的 configEpoch 中的最大值。这样,该 slave 成为 master 后,就会向所有节点发送广播包,强制其他节点更新相关 slots 的负责节点为自己。

Epoch Collision

2):每个节点的node epoch都是独一无二的;

3):拥有越高epoch的节点, 集群信息越新;

实际上, 在迁移slot或者使用cluster failover的时候, 如果多个节点同时bump epoch, 就有可能出现多个节点拥有同一个epoch, 违反上述原则(2)和(3). 这个时候拥有较小node id的节点就会自动再一次bump epoch, 以保证原则(3). 而原则(2)实际上因此也并不严格成立, 因为解决epoch collision需要一小段时间。

集群通信

集群消息通信通过集群总线通信,集群总线端口为客户端服务端口+10000(10000是固定值)

最开始,每个Redis实例自己是一个集群,我们通过cluster meet让各个结点互相“握手”。Redis Cluster目前缺少结点的自动发现功能。连接各个节点的工作使用CLUSTER MEET命令来完成,在一个实例上。

CLUSTER MEET

CLUSTER MEET命令实现:

1)节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

2)节点A根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息。

3)节点B接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

4)节点B向节点A返回一条PONG消息。

5)节点A收到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功的接收了自己发送的MEET消息。

6)之后,节点A将向节点B返回一条PING消息。

7)节点B将收到的节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功的接收到了自己返回的PONG消息,握手完成。

8)之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间后,节点B会被集群中的所有节点认识。

集群消息处理clusterProcessPacket

(cluster监听cluster端口,并通过clusterAcceptHandler接受集群节点发起的连接请求,通过aeCreateFileEvent将clusterReadHandler注册进事件回调,读取node发送的数据包。clusterReadHandler读取到完整的数据包后,调用clusterProcessPacket处理包请求)

1)更新接收消息计数器

2)查找发送者节点并且不是handshake(握手)节点

3)更新自己的epoch和slave的offset信息

4)处理MEET消息,使加入集群

5)从goosip中发现未知节点,发起handshake(握手)

6)对PING,MEET回复PONG

7)根据收到的心跳信息更新自己clusterState中的master-slave,slots信息

8)对FAILOVER_AUTH_REQUEST消息,检查并投票

9)处理FAIL,FAILOVER_AUTH_ACK,UPDATE信息

定时任务clusterCron

1)对handshake节点建立Link,发送Ping或Meet

2)向随机几点发送Ping(每个节点维护定时任务默认每秒执行10次,每秒会随机选取5个节点找出最久没有通信的节点发送ping消息,用于保证Gossip信息交换的随机性。每100毫秒都会扫描本地节点列表,如果发现节点最近一次接受pong消息的时间大于cluster_node_timeout/2,则立刻发送ping消息,防止该节点信息太长时间未更新)

3)如果是从查看是否需要做Failover

4)统计并决定是否进行slave的迁移,来平衡不同master的slave数

5)判断所有pfail报告数是否过半数

心跳数据(Gossip协议)

发送消息头信息Header

1)所负责slots的信息

2)主从信息

3)ip port信息

4)状态信息

发送其他节点Gossip信息

1)ping_sent, pong_received

2)ip, port信息

3)状态信息,比如发送者认为该节点已经不可达,会在状态信息中标记其为PFAIL或FAIL

clusterMsg结构的currentEpoch、sender、myslots等属性记录了发送者自身的节点信息,接收者会根据这些信息,在自己的clusterState.nodes字典里找到发送者对应的clusterNode结构,并对结构进行更新。

Redis集群中的各个节点通过Gossip协议来交换各自关于不同节点的状态信息,其中Gossip协议由MEET、PING、PONG三种消息实现,这三种消息的正文都由两个clusterMsgDataGossip结构组成。

每次发送MEET、PING、PONG消息时,发送者都从自己的已知节点列表中随机选出两个节点(可以是主节点或者从节点),并将这两个被选中节点的信息分别保存到两个结构中。

当接收者收到消息时,接收者会访问消息正文中的两个结构,并根据自己是否认识clusterMsgDataGossip结构中记录的被选中节点进行操作:

1.如果被选中节点不存在于接收者的已知节点列表,那么说明接收者是第一次接触到被选中节点,接收者将根据结构中记录的IP地址和端口号等信息,与被选择节点进行握手。

2.如果被选中节点已经存在于接收者的已知节点列表,那么说明接收者之前已经与被选中节点进行过接触,接收者将根据clusterMsgDataGossip结构记录的信息,对被选中节点对应的clusterNode结构进行更新。

发送消息数据结构

clusterNode结构保存了一个节点的当前状态,主要字段

1)slots:位图,由当前clusterNode负责的slot为1

2)salve, slaveof:主从关系信息

3)ping_sent, pong_received:心跳包收发时间

4)clusterLink *link:节点间的连接

5)list *fail_reports:收到的节点不可达投票

clusterState结构记录了在当前节点的集群目前所处的状态,主要字段

1)myself:指针指向自己的clusterNode

2)currentEpoch:当前节点的最大epoch,可能在心跳包的处理中更新

3)nodes:当前节点记录的所有节点,为clusterNode指针数组

4)slots:slot与clusterNode指针映射关系

5)migrating_slots_to,importing_slots_from:记录slots的迁移信息

6)failover_auth_time,failover_auth_count,failover_auth_sent,failover_auth_rank,failover_auth_epoch:Failover相关信息

--clusterNode结构

--clusterState结构

--clusterLink结构

故障发现

故障恢复类型

1)集群中一个Master节点故障之后,我们让该Master节点的子节点代替该Master节点继续向外提供服务。这个步骤我们叫slave promotion。

2)集群中一个Master节点没有Slave节点时(Orphaned Master),我们从其他有富余子节点的地方迁移过来一个子节点给这个孤立节点。

结点状态

节点状态:在线状态、PFAIL(疑似下线状态)、FAIL(已下线状态)

PFAIL:Redis的每个节点会不停的向其他节点发送PING消息来与其他节点同步信息的同时检测其他节点是否可达。我们以节点1的视角为例去介绍这个过程。当节点1与节点3建立连接之后,会不定时的向节点3发送PING命令,每次发送PING命令时节点1会记录发送命令的时刻ping_sent,同时会至多等待node_timeout/2来获得节点3的回复。正常情况下,节点3接收到PING命令后会给节点1返回PONG回复,但是此时节点3故障了,那么节点1会发现在等待node_timeout/2之后还是没有得到节点3的回复。这时节点1没有收到节点3的回复还有可能是他们之间本身连接出了问题,所以节点1会首先尝试与节点3重新建立连接。由于此时节点3处于故障状态,那么节点1就会重新建立连接失败。此时节点1会记录连接建立失败的时刻,在实现中,这个时间也是记录到ping_sent变量中。当节点1发现与节点3的断连时间超过了node_timeout之后,就会标记节点3为PFAIL,即Possible failure(PFAIL),可以中文意为主观下线。节点1标记节点3为PFAIL只是说明节点1认为节点3故障了,但并不代表节点3真正的故障了,因为或许是因为节点1和节点3之间的网络出了问题。当节点1标记节点3为PFAIL后,节点1会通过Gossip消息把这个信息发送给其他节点,接收到信息的节点会进行节点3客观下线状态判定。之前我们简单介绍过Redis的Gossip协议使用,节点1每次随机向其他几个节点发送自己视角下的部分节点状态信息。当节点2接收到来自节点1关于节点3的状态判定信息之后,节点2首先会把节点1加入到节点3的下线报告列表(Fail Report)中。每个节点都会维护一个下线报告列表,主要维护一个节点被哪些节点报告处于下线状态。比如节点4在节点1之前就向节点2报告了节点3的PFAIL信息,当节点2把节点1加入到节点3的下线报告后下线报告结果为:Node3:Node4,Node1,

客观下线状态的判定规则是: 当集群中有超过1/2数目的节点都认为节点3处于PFAIL,那么就判定节点3为FAIL。同时需要指出的是,节点1把Gossip消息发送给其他节点后,只有同样认为节点3处于PFAIL状态的节点才会去做客观下线状态判定。由于节点2也判定节点3处于PFAIL,所以节点2进入客观下线的判定。当节点2发现有一半以上(包括自己)的主节点都报告节点3处在PFAIL状态时,节点2标记节点3为FAIL状态,并立刻向集群所有节点广播这个信息。

广播消息

点2判定节点3为FAIL状态后,向全集群的节点广播Node3的故障消息CLUSTERMSG_TYPE_FAIL。当集群中的节点收到此消息时,都会标记节点3的状态为FAIL状态,包括节点3的两个子节点S1,S2也会标记节点3为FAIL状态。

redis 选主分析

资格检查:一个Slave节点过长时间不与Master节点通信,那么该节点就不具备参与竞选的资格。

休眠时间计算:当子节点都发现自己具备竞选资格时,就开始参与竞选,参与选举的节点首先随机休眠一段时间,每个节点一旦唤醒就立刻向所有的投票节点发起拉票请求。对于投票节点来说,每一轮选举中只能投出一票,投票的规则就是先到先得。所以一般情况下,都是休眠时间最短的节点容易获得大部分投票。

休眠时间由两部分组成:

一部分为固定的500ms时间,这500ms主要是为了等待集群状态同步。上面我们讲到节点2会向集群所有节点广播消息,那么这500ms就是等待确保集群的所有节点都收到了消息并更新了状态。

另一部分主要是一个随机的时间加上由该Slave节点的排名决定的附加时间,每个slave都会记录自己从主节点同步数据的复制偏移量。复制偏移量越大,说明该节点与主节点数据保持的越一致,所以我们按照更新状态的排序来确定休眠时间的附加部分。状态更新最近的节点SLAVE_RANK排名为1,那么其休眠的时间相应的也最短,也就意味着该节点最有可能获得大部分选票。DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds + SLAVE_RANK * 1000 milliseconds。

发起拉票&选举投票

假设S1先唤醒,S1唤醒后向所有节点发起拉票请求,即向其他节点发送CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST类型的消息。虽然所有的节点(主节点+子节点)都会收到拉票请求,但是只有主节点才具备投票资格。当其他主节点接收到拉票请求时,如果这一轮投票过程中该主节点没有投出自己的票,那么就会把自己的票投给S1,即向S1回复FAILOVER_AUTH_ACK消息。当S1接收到来自其他节点的ACK消息时会统计自己获得的票数,当S1发现自己收到集群中一半以上的主节点的投票时就会开始执行failover,即替换自己的主节点过程。

替换节点(节点3为旧Master,s1为旧master的slave)

首先标记自己为主节点,然后将原来由节点3负责的slots标记为由自己负责,最后向整个集群广播现在自己是Master同时负责旧Master所有slots的信息。其他节点接收到该信息后会更新自己维护的S1的状态并标记S1为主节点,将节点3负责的slots的负责节点设置为S1节点。

集群配置更新(节点3为旧Master,s1、s2为旧master的slave)

当S1成为了新的Master之后,我们是让S2和节点3成为新的主节点S1的Slave节点,去备份S1节点的数据。那这个过程是如何进行的呢?这是在各个节点信息更新的时候自动实现的。当节点3故障恢复重新上线后,发现原先本该由自己负责的slot被S1负责了,那么他就知道自己被替代了,会自动成为S1节点的子节点,当S2节点发现原先应该由其Master节点3负责的slot被S1负责了,那么他就知道自己的Master被替代了,就会成为S1的Slave节点。

redis cluster failOver过程中JedisCluster会报错,报错信息如下

hread-1] INFO  c.s.c.r.f.t.RedisClusterReadThread - time=2014-12-17 18:51:49

18:51:49.871 [pool-1-thread-1] ERROR c.s.c.r.f.t.RedisClusterReadThread - Too many Cluster redirections? key=900131

redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections? key=900131

at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:39) ~[jedis-2.6.2-sohutv-SNAPSHOT.jar:na]

18:51:51.979 [pool-1-thread-1] INFO  c.s.c.r.f.t.RedisClusterReadThread - time=2014-12-17 18:51:51

---------------------------------------------------(重复的异常太长了不打印了)----------------------------------------------------

---------------------------------------------------故障恢复----------------------------------------------------

18:52:02.620 [pool-1-thread-1] INFO  c.s.c.r.f.t.RedisClusterReadThread - success data time=2014-12-17 18:52:02

1):客户端故障时间&恢复时间:

服务器停机实际时间: 2014-12-17 18:51:42

客户端故障时间: 2014-12-17 18:51:43

恢复时间: 2014-12-17 18:52:02, 一共耗时19秒

手动故障转移

向从节点发送”CLUSTER  FAILOVER”命令,使其在主节点未下线的情况下,发起故障转移流程,升级为新的主节点,而原来的主节点降级为从节点;从节点发送”CLUSTER  FAILOVER”命令后,流程如下:

a:从节点收到命令后,向主节点发送CLUSTERMSG_TYPE_MFSTART包;

b:主节点收到该包后,会将其所有客户端置于阻塞状态,也就是在10s的时间内,不再处理客户端发来的命令;并且在其发送的心跳包中,会带有CLUSTERMSG_FLAG0_PAUSED标记;

c:从节点收到主节点发来的,带CLUSTERMSG_FLAG0_PAUSED标记的心跳包后,从中获取主节点当前的复制偏移量。从节点等到自己的复制偏移量达到该值后,才会开始执行故障转移流程:发起选举、统计选票、赢得选举、升级为主节点并更新配置;

CLUSTER  FAILOVER 命令支持2个参数FORCE和TAKEOVER,

FORCE:从节点不会与主节点进行交互,主节点也不会阻塞其客户端,而是从节点立即开始故障转移流程:发起选举、统计选票、赢得选举、升级为主节点并更新配置。

TAKEOVER:从节点不再发起选举,而是直接将自己升级为主节点,接手原主节点的槽位,增加自己的configEpoch后更新配置。

FORCE和TAKEOVER参数,主节点可以已经下线;而不使用任何选项,只发送”CLUSTER  FAILOVER”命令的话,主节点必须在线。

configEpoch冲突问题

如果一个主节点检测到另外一个主节点宣称自己具有相同的configEpoch

且如果节点的ID比另外一个宣称相同configEpoch的节点在逻辑上更小.

那么它会对自己的currentEpoch+1,然后用其结果作为新的configEpoch.

无论发生什么,只要有一个具有相同configEpoch的节点集合,除了具有最大节点ID的节点外,所有节点都会对自己的currentEpoch+1直到最终每个节点都去的了唯一的configEpoch.

这个机制同时确保了在新集群创建后,所有的节点起初都是设置了不同的configEpoch(即使实际上没有用)因为redis-trib工具会在启动时确保使用CONFIG SET-CONFIG-EPOCH.然而如果处于某种原因,节点没有被设置,它也会自动使用一个不同的配置epoch更新自己的配置.

数据迁移

数据迁移状态:MIGRATING状态、IMPORTING状态,当槽x从Node A向Node B迁移时,Node A和Node B都会有这个槽x,Node A上槽x的状态设置为MIGRATING,Node B上槽x的状态被设置为IMPORTING。

MIGRATING状态

1)如果key存在则成功处理

2)如果key不存在,则返回客户端ASK,客户端根据ASK首先发送ASKING命令到目标节点,然后发送请求的命令到目标节点

3)当key包含多个命令,

a)如果都存在则成功处理

b)如果都不存在,则返回客户端ASK

c)如果一部分存在,则返回客户端TRYAGAIN,通知客户端稍后重试,这样当所有的        key都迁移完毕的时候客户端重试请求的时候回得到ASK,然后经过一次重定向就          可以获取这批键

4)此时不刷新客户端中node的映射关系

IMPORTING状态

1)如果key不在该节点上,会被MOVED重定向,刷新客户端中node的映射关系

2)如果是ASKING命令则命令会被执行,key不在迁移的节点已经被迁移到目标的节点

3)Key不存在则新建

请求重定向

a)MOVED错误

1.请求的key对应的槽不在该节点上,节点将查看自身内部所保存的哈希槽到节点ID的映射记录,节点回复一个MOVED错误。

2.需要客户端进行再次重试。

b)ASK错误

1.请求的key对应的槽目前的状态属于MIGRATING状态,并且当前节点找不到这个key了,节点回复ASK错误。ASK会把对应槽的IMPORTING节点返回给你,告诉你去IMPORTING的节点尝试找找。

2.客户端进行重试首先发送ASKING命令,节点将为客户端设置一个一次性的标志(flag),使得客户端可以执行一次针对IMPORTING状态的槽的命令请求,然后再发送真正的命令请求。

3.不必更新客户端所记录的槽至节点的映射。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值