vlambda博客
学习文章列表

Redis5的高可用集群的水平扩展和集群选举原理的分析

每天与你分享 

IT编程开发 技术干货 架构方案 技术思维导图 设计模式 算法题库

正文内容

Redis5的高可用集群的水平扩展和集群选举原理的分析

高可用集群的水平扩展

Redis3.0以后的版本虽然有了集群功能,提供了比之前版本的哨兵模式更高的性能与可用性,但是集群的水平扩展却比较麻烦,今天就来带大家看看redis高可用集群如何做水平扩展,原始集群(见下图)由6个节点组成。

6个节点分布在一台机器上,采用三主三从的模式,以及进行水平新增的2个节点,一主一从。

实际应用中,最好用多台机器,比如说6个节点分布到3台机器上,redis在建立集群时为自动的将主从节点进行不同机器的分配,比如说:master-8001分布在192.168.5.100这台机器上,则它的slave-8004则不会在这台机器上,这是为了如果一台机器挂掉之后,还有其他的机器上的从节点进行替换master,以达到高可用的目的。

按之前的方法将集群进行启动。

分别启动6个节点:

/usr/local/redis/redis-5.0.2/src/redis-server /usr/local/redis-cluster/800*/redis.conf

查询启动情况:

ps -ef | grep redis

建立集群

/usr/local/redis/redis-5.0.2/src/redis-cli --cluster create --cluster-replicas 1 192.168.5.100:8001 192.168.5.100:8002 192.168.5.100:8003 192.168.5.100:8004 192.168.5.100:8005 192.168.5.100:8006

注意:如果之前redis集群给全部停掉了,这时候再建立集群时,会出现如下的情况

[ERR] Node 192.168.5.100:8001 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

这个时候需要将每个节点下的这几个文件给删掉(测试情况删掉,实际应用不要删,这是备份文件以及节点信息,按实际的情况进行处理

appendonly.aof dump.rdb nodes-8001.conf

客户端连接8001端口的redis实例

/usr/local/redis/redis-5.0.2/src/redis-cli -c -h 192.168.5.100 -p 8001

查看集群状态

192.168.5.100:8001> cluster infocluster_state:okcluster_slots_assigned:16384cluster_slots_ok:16384cluster_slots_pfail:0cluster_slots_fail:0cluster_known_nodes:6cluster_size:3cluster_current_epoch:6cluster_my_epoch:1cluster_stats_messages_ping_sent:607cluster_stats_messages_pong_sent:607cluster_stats_messages_sent:1214cluster_stats_messages_ping_received:602cluster_stats_messages_pong_received:607cluster_stats_messages_meet_received:5cluster_stats_messages_received:1214

查看节点信息

192.168.5.100:8001> cluster nodes194a31057d2e098483bcd2ad01e1bba6a1af6a7d 192.168.5.100:8006@18006 slave 7b5f6aa6dcb4d5aca4a94e57ddeea6971b38bba6 0 1544881722939 6 connectedb0db47b7bbd3694596f293aa522488882e8fe647 192.168.5.100:8004@18004 slave 33206e9384297092b5b8a85c944f3564e5d983d7 0 1544881722000 4 connected7b5f6aa6dcb4d5aca4a94e57ddeea6971b38bba6 192.168.5.100:8002@18002 master - 0 1544881721931 2 connected 5461-1092233206e9384297092b5b8a85c944f3564e5d983d7 192.168.5.100:8003@18003 master - 0 1544881719000 3 connected 10923-16383662809cf2d5bb138912dea7fb1e452f6e0f149da 192.168.5.100:8001@18001 myself,master - 0 1544881719000 1 connected 0-546060a0f7ced303374d8b36e7aa219cbcd4ff5b0caf 192.168.5.100:8005@18005 slave 662809cf2d5bb138912dea7fb1e452f6e0f149da 0 1544881720000 5 connected

这时候看一下这个信息

7b5f6aa6dcb4d5aca4a94e57ddeea6971b38bba6 192.168.5.100:8002@18002 master - 0 1544881721931 2 connected 5461-1092233206e9384297092b5b8a85c944f3564e5d983d7 192.168.5.100:8003@18003 master - 0 1544881719000 3 connected 10923-16383662809cf2d5bb138912dea7fb1e452f6e0f149da 192.168.5.100:8001@18001 myself,master - 0 1544881719000 1 connected 0-5460

介绍一下redis的hash槽的概念

从上图可以看出,整个集群运行正常,三个master节点和三个slave节点

  • 8001端口的实例节点存储0-5460这些hash槽

  • 8002端口的实例节点存储5461-10922这些hash槽

  • 8003端口的实例节点存储10923-16383这些hash槽

这三个master节点存储的所有hash槽组成redis集群的存储槽位,slave点是每个主节点的备份从节点,不显示存储槽位


Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数


这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

  • 使用哈希槽的好处就在于可以方便的添加或移除节点。

  • 当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;

  • 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;

  • 在这一点上,我们以后新增或移除节点的时候不用先停掉所有的 redis 服务。


用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样做的原因是为什么呢?

Redis Cluster是自己做的crc16的简单hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单,节点增删时处理起来也很方便。

为了动态增删节点的时候,不至于丢失数据么?

节点增删时不丢失数据和hash算法没什么关系,不丢失数据要求的是一份数据有多个副本。

还有集群总共有2的14次方,16384个哈希槽,那么每一个哈希槽中存的key 和 value是什么?

当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。


Redis 5 集群选举原理分析

在此之前先说下这个参数

cluster-node-timeout

   真实世界的机房网络往往并不是风平浪静的,它们经常会发生各种各样的小问题。比如网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。

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

开始,先重现主从切换
先查看一下当前集群节点信息

[root@localhost redis-cluster]# /usr/local/redis/redis-5.0.2/src/redis-cli -c -h 192.168.5.100 -p 8001192.168.5.100:8001> cluster nodes412b26f846f27a63484594af931b9fb3b612ee9c 192.168.5.100:8003@18003 master - 0 1544973259457 3 connected 10923-163836c2000bf49a6e8229e518432c74d222521ff2f41 192.168.5.100:8005@18005 slave 1204327cbb9eaf4c9be0b90880531f6861e65f13 0 1544973258448 5 connected52c723f6d391bc2975b27a5210451a2cf590d939 192.168.5.100:8002@18002 master - 0 1544973258000 2 connected 5461-109221d8f23d4ed7ccdfb5a6f2d8b9b6286cfd25d1d4b 192.168.5.100:8006@18006 slave 52c723f6d391bc2975b27a5210451a2cf590d939 0 1544973260466 6 connected1204327cbb9eaf4c9be0b90880531f6861e65f13 192.168.5.100:8001@18001 myself,master - 0 1544973257000 1 connected 0-5460886c96bfdef55b2fbcfee6eceb77fcf294fdfe33 192.168.5.100:8004@18004 slave 412b26f846f27a63484594af931b9fb3b612ee9c 0 1544973257440 4 connected192.168.5.100:8001> 

这时候我们kill掉一个master,再查看一下节点信息

[root@localhost redis-cluster]# /usr/local/redis/redis-5.0.2/src/redis-cli -c -h 192.168.5.100 -p 8002192.168.5.100:8002> cluster nodes6c2000bf49a6e8229e518432c74d222521ff2f41 192.168.5.100:8005@18005 slave 1204327cbb9eaf4c9be0b90880531f6861e65f13 0 1544973521650 5 connected1d8f23d4ed7ccdfb5a6f2d8b9b6286cfd25d1d4b 192.168.5.100:8006@18006 slave 52c723f6d391bc2975b27a5210451a2cf590d939 0 1544973521000 6 connected886c96bfdef55b2fbcfee6eceb77fcf294fdfe33 192.168.5.100:8004@18004 slave 412b26f846f27a63484594af931b9fb3b612ee9c 0 1544973522659 4 connected412b26f846f27a63484594af931b9fb3b612ee9c 192.168.5.100:8003@18003 master - 0 1544973520000 3 connected 10923-163831204327cbb9eaf4c9be0b90880531f6861e65f13 192.168.5.100:8001@18001 master - 1544973509291 1544973508479 1 disconnected 0-546052c723f6d391bc2975b27a5210451a2cf590d939 192.168.5.100:8002@18002 myself,master - 0 1544973522000 2 connected 5461-10922192.168.5.100:8002> cluster nodes6c2000bf49a6e8229e518432c74d222521ff2f41 192.168.5.100:8005@18005 master - 0 1544973547997 7 connected 0-54601d8f23d4ed7ccdfb5a6f2d8b9b6286cfd25d1d4b 192.168.5.100:8006@18006 slave 52c723f6d391bc2975b27a5210451a2cf590d939 0 1544973549010 6 connected886c96bfdef55b2fbcfee6eceb77fcf294fdfe33 192.168.5.100:8004@18004 slave 412b26f846f27a63484594af931b9fb3b612ee9c 0 1544973546000 4 connected412b26f846f27a63484594af931b9fb3b612ee9c 192.168.5.100:8003@18003 master - 0 1544973543000 3 connected 10923-163831204327cbb9eaf4c9be0b90880531f6861e65f13 192.168.5.100:8001@18001 master,fail - 1544973509291 1544973508479 1 disconnected52c723f6d391bc2975b27a5210451a2cf590d939 192.168.5.100:8002@18002 myself,master - 0 1544973548000 2 connected 5461-10922192.168.5.100:8002> 
我们看到这时候,8001显示的是fail,而8005则选举为master,这时候我们将8001重新启动,查看一下节点信息这时候8001位slave,它的master:6c2000bf49a6e8229e518432c74d222521ff2f41,也就是8005,可见redis的高可用还是靠谱的。
192.168.5.100:8002> cluster nodes6c2000bf49a6e8229e518432c74d222521ff2f41 192.168.5.100:8005@18005 master - 0 1544973819402 7 connected 0-54601d8f23d4ed7ccdfb5a6f2d8b9b6286cfd25d1d4b 192.168.5.100:8006@18006 slave 52c723f6d391bc2975b27a5210451a2cf590d939 0 1544973821440 6 connected886c96bfdef55b2fbcfee6eceb77fcf294fdfe33 192.168.5.100:8004@18004 slave 412b26f846f27a63484594af931b9fb3b612ee9c 0 1544973822000 4 connected412b26f846f27a63484594af931b9fb3b612ee9c 192.168.5.100:8003@18003 master - 0 1544973821000 3 connected 10923-163831204327cbb9eaf4c9be0b90880531f6861e65f13 192.168.5.100:8001@18001 slave 6c2000bf49a6e8229e518432c74d222521ff2f41 0 1544973822450 7 connected52c723f6d391bc2975b27a5210451a2cf590d939 192.168.5.100:8002@18002 myself,master - 0 1544973821000 2 connected 5461-10922192.168.5.100:8002> 
原理分析:

当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:

slave发现自己的master变为FAIL将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack尝试failoverslave收集FAILOVER_AUTH_ACK超过半数后变成新Master广播Pong通知其他集群节点。

从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票


延迟计算公式:

DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms

SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。


跳转重定位



[root@localhost 8001]# /usr/local/redis/redis-5.0.2/src/redis-cli -c -h 192.168.5.100 -p 8003192.168.5.100:8003> get name-> Redirected to slot [5798] located at 192.168.5.100:8002"xxx"192.168.5.100:8002>

推荐阅读