【深入理解】Redis高可用架构之集群架构

系列文章目录

真正说透Redis五种数据结构
Redis持久化之RDB+AOF+混合持久化实战演练
Redis高可用之主从架构
Redis高可用之哨兵架构实战


文章目录

  • 系列文章目录
  • 前言
  • 一、 为什么需要Redis集群
  • 二、 Redis集群的优势
  • 三、 集群原理
    • 1、数据的分片
    • 2、节点的通信
    • 3、集群选举
    • 4、访问重定向
  • 四、集群中的瓶颈
    • 1、节点通信的成本
    • 2、数据倾斜
  • 五、集群安装中的注意点
    • 1、docker内网无法通信
    • 2、节点故障集群是否可用
  • 总结


前言


前面文件介绍了Redis的主从以及哨兵架构的搭建和原理,如需了解的请查看前面的文章,在Redis3.0版本之前,是通过哨兵架构来监控节点状态以及故障转移的,在大部分场景中哨兵架构就已经满足日常场景需要,但是在一些互联网场景下,哨兵架构的下的redis单机性能就会出现瓶颈。在Redis3.0之后,Redis推出了集群的分布式解决方案,主要解决单机性能的瓶颈,本文就针对整合网上以及官网上对Redis集群的原理做一个分享。

一、 为什么需要Redis集群

  1. 主从架构的出现
    我们知道Redis的是使用单线程的(在Redis6.0也推出了多线程的版本),其性能也是非常高,但是也是由于redis是单线程的,在读多写少的情况下,为了读的线程不能影响写的线程,所有有了主从架构,主从架构帮我们主要解决了两个问题,1、数据的备份 2、读写可以分离
  2. 哨兵架构的出现
    在高可用方案,并不是永不宕机,而是一年中99.9%应用都处于可用状态,所有主从架构下,如果出现Redis主节点宕机,那么就需要人工干预节点的启动,也可以使用脚本来快速完成节点的启动,那如果机器坏了,还得将从节点改为主节点,包括修改客户端连接,这样下来,也就达不到高可用方案了。在Redis2.8版本下引入了哨兵模式,哨兵模式主要帮我们完成主从架构下存在的问题,通过哨兵监控、切换、通知来保证Redis达到真是的高可用
  3. 集群架构的出现
    哨兵架构基本上能满足大多数公司的使用,由于Redis是单线程的,所有的操作都是按序进行的,在一些高并发场景中,哨兵架构也会存在问题,单节点的写入性能瓶颈,单节点的容量问题。所有Redis集群也能很好的解决这两个问题

二、 Redis集群的优势

  1. 单节点并发瓶颈
    Redis 集群是一种分布式数据库方案,集群通过分片来进行数据管理,并可以完成主从复制和故障转移功能。
    【深入理解】Redis高可用架构之集群架构_第1张图片
    Redis将数据按照槽位分布在不同的节点上,这样保证了每个节点只处理部分数据,即使在分区的情况下,导致某个节点不可用,那也就只影响部分数据的操作,这样就大大提高了系统的高可用和高并发。

  2. 单节点容量瓶颈
    【深入理解】Redis高可用架构之集群架构_第2张图片

我们都知道Redis的数据都是存储在磁盘上的,默认的也是混合持久化方式,Redis会Fork一个子进程完成持久化,如果节点的数据量达到上百GB的时候,这个持久化的过程是非常耗时,并且占用CPU,这种情况下会导致Redis出现卡顿的情况,包括在主从同步时,也是非常慢的,很可能导致超时而同步失败。那么怎么解决呢?
这个时候就无法使用垂直扩展了,不能再加大机器的内存等配置了,所以这个时候就很需要Redis集群架构,通过增加实例来将数据进行水平拆分,将单节点的数据量分摊到每个节点上,这样就能解决上述问题

三、 集群原理

1、数据的分片

1、槽位的分配
在创建集群时,Redis 会自动将 16384 个 哈希槽平均分布在集群实例上,也可以手动进行槽位的分配,比如某些机器的配置高,这样就可以分配多一些槽位。Redis是通过CRC16算法对key进行hash运算,然后在对16384进行取模的到真正的槽位。

HASH_SLOT = CRC16(key) mod 16384

切记,当 16384 个槽都分配完全,Redis 集群才能正常工作,如果槽位未分配完,在执行命令时会报如下错

(error) CLUSTERDOWN The cluster is down

2、hash标签
在一些场景中,我们希望一些不同的key可以分布到同一个节点上,这个时候hash标签就派上用场了。
为了实现哈希标签,哈希槽是用另一种不同的方式计算的。基本来说,如果一个键包含一个 “{…}”这样的模式,只有 { 和 } 之间的字符串会被用来做哈希以获取哈希槽,注意,是第一个{ 和第一个 }之间的数据参与hash槽位的计算

2、节点的通信

Redis集群节点间采取gossip协议进行通信,也叫集群总线(端口默认为节点的端口+10000,如6379节点的通信端口为16379),这种协议通信是不需要想zookeeper那样集中式管理元数据。

gossip协议包含多种消息,包括ping,pong,meet,fail等等

  • meet:某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信
  • ping:每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据(类似自己感知到的集群节点增加和移除,hash slot信息等)
  • pong: 对ping和meet消息的返回,包含自己的状态和其他信息,也可以用于信息广播和更新
  • fail: 某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了


集中式的优点在于元数据的更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的时候立即就可以立即感知到;不足在于所有的元数据的更新压力全部集中在一个地方,可能导致元数据的存储压力。 很多中间件都会借助zookeeper集中式存储元数据。

gossip协议的优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后

3、集群选举

集群中各个节点都是通过gossip协议来进行节点通信的,当某个节点无法ping通时,发起ping的节点就会标记此节点故障,然后会告知其他节点此节点的故障信息,如果整个集群中大多数节点都判断此节点故障,则会进行故障转移操作。
从节点满足以下情况才允许参与选举

  • 这个主节点负责的哈希槽数目不为零
  • 从节点和主节点之间的重复连接(replication link)断线不超过一段给定的时间,这是为了确保从节点的数据是可靠的

当一个 Slave 发现自己的主节点进入已下线状态后,从节点将开始对下线的主节点进行故障转移

  1. slave发现自己的master变为FAIL
  2. 将集群的配置纪元 +1,每次执行故障转移都会 +1
  3. slave向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息、并且具有投票权的master向这个slave投票
  4. 收到消息master判断自己在这个纪元内尚未投票给其它slave,那么master将向这个slave返回一条FAILOVER_AUTH_REQUEST消息,表示同意这个slave成为新的master,若已投票则不会重复投票
  5. slave节点会收到其它master的 FAILOVER_AUTH_ACK消息,如果收集到的票 >= (N/2) + 1 支持,那么这个slave就被选举为新的master
  6. 如果在一个配置纪元里面没有slave能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的master为止
  7. 选举成功后,新的master广播pong消息通知其他集群节点
    【深入理解】Redis高可用架构之集群架构_第3张图片

从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是使用Raft算法算出一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票
延迟计算公式
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举

4、访问重定向

问题1:客户端又怎么确定访问的数据到底分布在哪个实例上呢?
Redis节点在通过 Gossip 协议与集群中其它节点通信时,会发生槽位对应的节点信息,这样所有节点都知道槽位对应节点地址的信息。
客户端在请求时,Redis服务返回所有的槽位与对应的实例信息,客户端可以将这些信息保存在本地,然后在请求时,客户端根据key计算出对应的槽位信息,这样能准确的将信息发生到对应的实例上,提高命中率和效率。

问题2:在新增节点或者移动槽位后,可能导致之前客户端存的槽位映射不准确,客户端则命中错误的节点
客户端命中错误的节点后,由于集群节点不能代理(proxy)请求,redis服务端会反馈客户端正确的节点信息,重定向分为两种情况 MOVEDASK,详细可以参考官网上的解释

  • 槽位分配完成
    这个时候如果客户端命中旧的节点,节点会返回MOVED并告知正确的节点信息,客户端收到后,会请求正确的节点, 然后再更新本地存储的槽位信息
  • 槽位正在分配中
    如果访问的槽位正在迁移中呢,那么此时客户端访问当前节点时:
  • 首先判断数据是否在当前节点中,如果是则返回
  • 如果数据不在当前节点中,那么则会返回客户端ASK
  • 客户端收到ASK后,会向目标节点发生一个一次性请求
  • 客户端再发生实际的操作命令道目标节点,并且不会更新本地存储的槽位信息

个人的理解:客户端请求服务端,如果数据存在,则直接返回,如果不存在,则判断槽位是否正在迁移中,如果不是,则表示迁移完成,反馈客户端MOVED重定向,客户端收到后会更新本地槽位信息,然后请求新的节点。如果在迁移中,则反馈客户端ASK,客户端收到后不会更新槽位信息,然后会请求新的节点

【深入理解】Redis高可用架构之集群架构_第4张图片

四、集群中的瓶颈

1、节点通信的成本

Redis集群中的节点并不是越多越好,官方给出的建议上线是1000个节点。
主要原因由前面可知,集群间的通信是gossip协议,是无中心化的,在一个有 N 个节点的集群中,每个节点都有 N-1 个流出的 TCP 连接,和 N-1 个流入的连接。 这些 TCP 连接会永久保持,并不是按需创建的。
那么当节点越多,通信的成本就越大,包括选举耗时也会变长。所以合理安排集群节点的数量有利于系统的稳定性

2、数据倾斜

虽然集群有效的将数据分散到不同的实例上,但是对于热点数据,比如促销等活动时,就很容易产生数据落在其中一台机器上,这样就造成了单台机器的压力过大,很容易产生雪崩的情况。数据倾斜分为两种情况

  1. big key造成的单个实例的容量过载
    解决:可以使用工具或者redis命令来发下big key,然后首先在业务上尽量避免bigkey,其实就是对bigkey进行拆分
  2. hot key造成的单个实例QPS过载
    解决:如果这类数据写少读多的情况,可以使用多副本来分摊读的压力,如果不是则需要将key进行封装,生成不同前缀的key,尽量让数据分散到不同的机器上

五、集群安装中的注意点

1、docker内网无法通信

在docker安装集群时,客户端可能会访问节点异常,原因是节点暴露的是容器内的IP,
【深入理解】Redis高可用架构之集群架构_第5张图片
在配置文件中暴露机器的IP+端口

集群节点IP地址
cluster-announce-ip 121.36.xxx.xxx
节点映射端口
cluster-announce-port 6371

2、节点故障集群是否可用

节点故障集群是否可用如果某个主节点没有从节点,那么当它发生故障时,集群将完全处于不可用状态。不过 Redis 也提供了一个参数cluster-require-full-coverage可以允许部分节点故障,其它节点还可以继续提供对外访问


总结

本文就redis集群中的概念和原理做了分析,在高可用方面,redis提供了主从、哨兵、集群三种方式供大家选择,其复杂和维护成本也是逐渐变大,具体使用请大家结合自己的业务,选择合适的节点部署。

你可能感兴趣的:(Redis,redis,架构,缓存,redis集群,redis高可用)