Redis深度历险-Redis集群Cluster

本文大部分内容引自《Redis深度历险:核心原理和应用实践》,感谢作者!!!

Gossip协议

https://www.jianshu.com/p/54eab117e6ae

RedisCluster

1、RedisCluster将所有数据划分为16384个槽(slots),每个节点负责其中一部分槽位,槽位信息存储于每个节点中

2、RedisCluster的客户端连接集群时会得到一份集群的槽位配置信息,当客户端要查找某个key时可以直接定位到目标节点

3、RedisCluster的每个节点会将集群的配置信息持久化到配置文件中

槽位定位算法

1、Cluster默认会对key的值使用crc32算法(https://www.cnblogs.com/masonzhang/p/10261855.html)进行hash得到一个整数值,然后使用这个整数值对16384取模得到具体槽位

2、Cluster允许用户强制某个key在特定槽上,通过在key中签入tag标记就可以强制key所在的槽位等于tag所在的槽位

def HASH_SLOT(key)
    s = key.index "{"
    if s
        e = key.index "}",s+1
        if e && e != s+1
            key = key[s+1..e-1]
        end
    end
    crc16(key) % 16384
end

跳转

1、当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据

2、MOVED 指令的第一个参数 3999 是 key 对应的槽位编号,后面是目标节点地址。MOVED 指令前面有一个减号,表示该指令是一个错误消息;客户端收到 MOVED 指令后,要立即纠正本地的槽位映射表。后续所有 key 将使用新的槽位映射表

GET x
-MOVED 3999 127.0.0.1:6381

迁移

1、Redis迁移的单位是槽,Redis一个槽一个槽进行迁移;当一个槽在迁移时这个槽就处于中间过渡状态,槽的原节点状态为migration,在目标节点的状态为importing,表示数据正在从源流向目标

2、在迁移中redis-trib首先会在源和目标节点设置好中间过渡状态,然后一次性获取源节点所有key列表(keysinslot指令),然后对每个key进行迁移;原节点对当前的key执行dump指令(https://www.runoob.com/linux/linux-comm-dump.html)得到序列化内容,然后向目标节点发送restore指令携带序列化的内容作为参数,目标节点进行反序列化讲内容恢复到目标节点内存中,然后返回原节点OK,原节点收到目标节点返回的OK后把当前的key删除掉就完成了key迁移的整个过程

从源节点获取内容 => 存到目标节点 => 从源节点删除内容

3、迁移过程是同步的,在目标节点执行restore指令到原节点删除key之间,原节点的主线程会处于阻塞状态直到key被成功删除;在迁移的过程中如果突然出现网络故障,整个slot的迁移只进行了部分;这时两个节点仍然处于中间过渡状态,下次迁移工具重新连上时会提示用户继续进行迁移;迁移大key,会阻塞Redis主线程,因为migrate是阻塞指令

迁移过程中客户端访问流程的变化

1、新旧节点中的槽位都会存在部分key数据,客户端访问时会首先尝试访问旧节点,如果对应数据在旧节点中就会正常处理;如果不在旧节点中就会出现两种情况:1、数据在新节点中,2、数据不存在;旧节点不知道是哪种情况,所以旧节点会向客户端返回一个-ASK targetNodeAddr的重定向指令,客户端收到重定向指令后回去目标节点执行一个不带参数的asking指令,然后在目标节点重新执行原先的操作指令

2、客户端收到重定向指令后回去目标节点执行一个不带参数的asking指令的原因

在key迁移没有完成之前槽位还不属于新节点管理,如果这个时候向目标结点发送该槽位的指令,他会向客户端返回一个-MOVED重定向指令告诉源节点去执行,会行程重定向循环。asking指令的目的就是告诉目标节点下一条指令不能不处理,而是要当成自己的槽位来处理

3、迁移是会影响服务效率的,同样的指令在正常情况下一个 ttl 就能完成,而在迁移中得 3 个 ttl 才能搞定

节点容错

Redis Cluster 可以为每个主节点设置若干个从节点,单主节点故障时,集群会自动将其中某个从节点提升为主节点。如果某个主节点没有从节点,那么当它发生故障时,集群将完全处于不可用状态。不过 Redis 也提供了一个参数cluster-require-full-coverage(https://blog.51cto.com/liuzhanbin/1871514)可以允许部分节点故障,其它节点还可以继续提供对外访问

网络抖动

Redis Cluster 提供了一种选项cluster-node-timeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。

还有另外一个选项cluster-slave-validity-factor作为倍乘系数来放大这个超时时间来宽松容错的紧急程度。如果这个系数为零,那么主从切换是不会抗拒网络抖动的。如果这个系数大于 1,它就成了主从切换的松弛系数。单位为ms,默认值为15000

可能下线 (PFAIL-Possibly Fail) 与确定下线 (Fail)

1、Redis Cluster 是去中心化的,一个节点认为某个节点失联了并不代表所有的节点都认为它失联了。所以集群还得经过一次协商的过程,只有当大多数节点都认定了某个节点失联了,集群才认为该节点需要进行主从切换来容错

2、Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换

槽位感知迁移

重试一次

1、第一个 moved 是用来纠正槽位的。如果我们将指令发送到了错误的节点,该节点发现对应的指令槽位不归自己管理,就会将目标节点的地址随同 moved 指令回复给客户端通知客户端去目标节点去访问。这个时候客户端就会刷新自己的槽位关系表,然后重试指令,后续所有打在该槽位的指令都会转到目标节点

2、第二个 asking 指令和 moved 不一样,它是用来临时纠正槽位的。如果当前槽位正处于迁移中,指令会先被发送到槽位所在的旧节点,如果旧节点存在数据,那就直接返回结果了,如果不存在,那么它可能真的不存在也可能在迁移目标节点上。所以旧节点会通知客户端去新节点尝试一下拿数据,看看新节点有没有。这时候就会给客户端返回一个 asking error 携带上目标节点的地址。客户端收到这个 asking error 后,就会去目标节点去尝试。客户端不会刷新槽位映射关系表,因为它只是临时纠正该指令的槽位信息,不影响后续指令

 

重试二次

moved 和 asking 指令都是重试指令,客户端会因为这两个指令多重试一次。客户端有可能重试 2 次这种情况是存在的,比如一条指令被发送到错误的节点,这个节点会先给你一个 moved 错误告知你去另外一个节点重试。所以客户端就去另外一个节点重试了,结果刚好这个时候运维人员要对这个槽位进行迁移操作,于是给客户端回复了一个 asking 指令告知客户端去目标节点去重试指令。所以这里客户端重试了 2 次

集群变更感知

当服务器节点变更时,客户端应该即时得到通知以实时刷新自己的节点关系表。那客户端是如何得到通知的呢?这里要分 2 种情况:

1、目标节点挂掉了,客户端会抛出一个 ConnectionError,紧接着会随机挑一个节点来重试,这时被重试的节点会通过 moved error 告知目标槽位被分配到的新的节点地址

2、运维手动修改了集群信息,将 master 切换到其它节点,并将旧的 master 移除集群。这时打在旧节点上的指令会收到一个 ClusterDown 的错误,告知当前节点所在集群不可用 (当前节点已经被孤立了,它不再属于之前的集群);这时客户端就会关闭所有的连接,清空槽位映射关系表,然后向上层抛错;待下一条指令过来时,就会重新尝试初始化节点信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值