写点什么

🚄【Redis 干货领域】帮你完全搞定 Cluster 原理(架构篇)

发布于: 2021 年 05 月 22 日
🚄【Redis 干货领域】帮你完全搞定Cluster原理(架构篇)

每日一句


自律是最强者的本能,当你把自律作为习惯,那你已经成为强者。

前提概要

为什么需要 Redis 集群?


  • 在讲 Redis 集群架构之前,我们先简单讲下 Redis 单实例的架构,从最开始的一主 N 从,到读写分离,再到 Sentinel 哨兵机制,单实例的 Redis 缓存足以应对大多数的使用场景,也能实现主从故障迁移。


在某些场景下,单实例存 Redis 缓存会存在的几个问题:

Redis 主从架构+Sentinel 仍存在问题

写并发的压力仍在


  • Redis 单实例读写分离可以解决读操作的负载均衡,但对于写操作,仍然是全部落在了 master 节点上面,在海量数据高并发场景,一个节点写数据容易出现瓶颈,造成 master 节点的压力上升

海量数据的存储压力


  • 内存容量的限制Redis 的最大缺点和局限性就在于内存存储数据,这样子对容量而言会有相当大的限制

  • 持久化和硬盘的限制Redis 单实例本质上只有一台 Master 作为存储,如果面对海量数据的存储,一台 Redis 的服务器就应付不过来了,而且数据量太大意味着持久化成本高,严重时可能会阻塞服务器,造成服务请求成功率下降,降低服务的稳定性


针对以上的问题,Redis 集群提供了较为完善的方案,解决了存储能力受到单机限制,写操作无法负载均衡的问题。

什么是 Redis 集群?


【Redis 集群】是一种服务器 Sharding(分片)技术,3.0 版本开始正式提供。


Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在 redis3.0 上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容


  • (分片存储)Redis3.0 加入了 Redis 的集群模式实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的 master 节点上面,从而解决了海量数据的存储问题

  • (指令转换)Redis 集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一 Redis 实例一样,不需要任何代理中间件,当客户端操作的 key 没有分配到该 node 上时,Redis 会返回转向指令,指向正确的 Redis 节点

  • (主从和哨兵)Redis 也内置了高可用机制,支持 N 个 master 节点,每个 master 节点都可以挂载多个 slave 节点,当 master 节点挂掉时,集群会提升它的某个 slave 节点作为新的 master 节点



如上图所示,Redis 集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有 master 节点具有处理请求的能力,slave 节点主要是用于节点的高可用

集群的数据分片

前面讲到,Redis 集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的 Redis 服务器上。常见的分区算法有 hash 算法、一致性 hash 算法,关于这些算法这里就不多介绍。

如果要实现 Redis 数据的分片,我们有三种方案。

  • (客户端负载)第一种是在客户端实现相关的逻辑,例如用取模或者一致性哈希对 key 进行分片,查询和修改都先判断 key 的路由

    Jedis 客户端提供了 Redis Sharding 的方案,并且支持连接池

    Sharded 分片的原理?怎么连接到某一个 Redis 服务:提供了一致性 hash 和 md5 散列两种 hash 算法,默认使用一致性 hash 算法。

    并且为了使得请求能均匀的落在不同的节点上,Sharded Jedis 会使用节点的名称(如果节点没有名称使用默认名称)虚拟化出 160 个虚拟节点。也可以根据不同节点的 weight,虚拟化出 160,weight 个节点

    当客户端访问 redis 时,首先根据 key 计算出其落在哪个节点上,然后找到节点的 ip 和端口进行连接访问


  • (中间代理负载)第二种是把做分片处理的逻辑抽取出来,运行一个独立的代理服务,客户端连接到这个代理服务,代理服务做请求的转发典型的代理分区方案有 Twitter 开源的 Twemproxy 和国内的豌豆荚开源的 Codis


  • (服务端负载)第三种就是基于服务端实现

普通 hash 算法

  • 如果是希望数据分布相对均匀的话,可以考虑哈希后取模

  • 将 key 使用 hash 算法计算之后:hash(key)%N,根据余数,决定映射到那一个节点

  • 优点就是比较简单,属于静态的分片规则。但是一旦节点数量变化,新增或者减少,由于取模的 N 发生变化, 数据需要重新分布和迁移

一致性 hash 算法

  • 把所有的哈希值空间组织成一个虚拟的圆环(哈希环),整个空间按顺时针方向组织。因为是环形空间,0 和 2^32-1 是重叠的(总共 2^32 个)

    先根据机器的名称或者 IP 计算哈希值

    然后分布到哈希环中。查找时先根据 key 计算哈希值,得到哈希环中的位置

    最后顺时针找到第一个大于等于该哈希值的第一个 Node,就是数据存储的节点

    优点是在加入和删除节点时只影响相邻的两个节点

    缺点是加减节点会造成部分数据无法命中

    此外,针对于 hash 节点分散不均匀或者倾倒状态,采用一个节点分为多个虚拟节点做优化

  • 所以一般用于缓存,而且用于节点量大的情况下,扩容一般增加一倍节点保障数据负载均衡

哈希槽 Slot 算法


Redis 集群既没有用哈希取模,也没有用一致性哈希,而是用 Hash 槽来实现的。Redis 集群创建了 16384 个槽(slot),每个节点负责一定区间的 slot

Redis 集群的哈希槽的分区


  • Redis 集群中有 163842^14)个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的 Redis 节点上面进行管理,也就是说每个 Redis 节点只负责一部分区间的哈希槽。

  • 对数据进行操作的时候:

    集群会对使用 CRC16 算法对 key 进行计算并对 16384 取模 (slot = CRC16(key)%16384) 。】

    得到的结果就是 Key-Value 所放入的槽,通过这个槽值,去找对应的槽所对应的 Redis 节点

    然后直接到这个对应的节点上进行存取操作

    好处:(自动更新 hash 槽的映射数据关系)使用哈希槽的好处就在于可以方便的添加或者移除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了

哈希槽分区的分析

集群的每个节点负责一部分 hash 槽,举个例子,比如当前集群有 3 个节点,那么

  • 节点 A 包含 0 到 5460 号哈希槽

  • 节点 B 包含 5461 到 10922 号哈希槽

  • 节点 C 包含 10923 到 16383 号哈希槽

这种结构很容易添加或者删除节点。

  • (1)如果我想新添加个节点 D , 我需要从节点 A, B, C 中得部分槽到 D 上

  • (2)如果我想移除节点 A ,需要将 A 中的槽移到 B 和 C 节点上,然后将没有任何槽的 A 节点从集群中移除即可

由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态

查看 key 属于哪个 slot:

redis> cluster keyslot yan
复制代码

注意:key 与 slot 的关系是永远不会变的,会变的只有 slot 和 Redis 节点的关系

(tips) 怎么让相关的数据落到同一个节点上

在 key 里面加入{hash tag}即可。Redis 在计算槽编号的时候只会获取{}之间的字符串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可以被计算出相同的槽

客户端重定向

客户端连接到哪一台服务器?访问的数据不在当前节点上,怎么办?

比如在 7291 端口的 Redis 的 redis-cli 客户端操作:

127.0.0.1:7291> set qs 1
(error) MOVED 13724 127.0.0.1:7293
复制代码
  1. 服务端返回 MOVED,也就是根据 key 计算出来的 slot 不归 7191 端口管理,而是 归 7293 端口管理,服务端返回 MOVED 告诉客户端去 7293 端口操作

  2. 这个时候更换端口,用 redis-cli –p 7293 操作,才会返回 OK。这样客户端需要连接两次。或者用./redis-cli -c -p port 的命令(c 代表 cluster)自动重定向

新增或下线了 Master 节点,数据怎么迁移(重新分配)?

  • 因为 key 和 slot 的关系是永远不会变的,当新增了节点的时候,需要把原有的 slot 分配给新的节点负责,并且把相关的数据迁移过来

  • 新增的节点没有哈希槽,不能分布数据,在原来的任意一个节点上执行。

redis-cli --cluster reshard 127.0.0.1:7291
复制代码
  • 槽的迁移与指派命令

CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000 
复制代码

输入需要分配的哈希槽的数量(比如 500),和哈希槽的来源节点(可以输入 all 或 者 id)

集群的主从复制模型

  • 为了保证高可用,redis-cluster 集群引入了主从复制模型,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点

    当其它主节点 ping 一个主节点 A 时,如果半数以上的主节点与 A 通信超时,那么认为主节点 A 宕机了如果主节点 A 和它的从节点 A1 都宕机了,那么该集群就无法再提供服务了。

  • 默认情况下,redis 集群的读和写都是到 master 上去执行的,不支持 slave 节点读和写,跟 Redis 主从复制下读写分离不一样,因为 redis 集群的核心的理念,主要是使用 slave 做数据的热备,以及 master 故障时的主备切换,实现高可用的。

  • Redis 的读写分离,是为了横向任意扩展 slave 节点去支撑更大的读吞吐量。而 redis 集群架构下,本身 master 就是可以任意扩展的,如果想要支撑更大的读或写的吞吐量,都可以直接对 master 进行横向扩展。

集群的特点



  • redis 集群内部的节点是相互通信的(PING-PONG 机制),每个节点都是一个 redis 实例;内部使用二进制协议优化传输速度和带宽。

  • 为了实现集群的高可用,即判断节点是否健康(能否正常使用),redis-cluster 有这么一个投票容错机制:如果集群中超过半数的节点投票认为某个节点挂了,那么这个节点就挂了(fail)。这是判断节点是否挂了的方法

  • Redis 集群是没有统一的入口的,客户端与 Redis 节点直连,不需要中间代理层,也不需要连接集群所有节点,即连接集群中任何一个可用节点即可。

  • 客户端(client)连接集群的时候连接集群中的任意节点(node)即可。

  • 那么如何判断集群是否挂了呢? -> 如果集群中任意一个节点挂了,而且该节点没有从节点(备份节点),那么这个集群就挂了。这是判断集群是否挂了的方法;

集群运行的要求

  • Redis 集群至少需要 3 个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以 2 个节点无法构成集群

  • 要保证集群的高可用,需要每个节点都有从节点,也就是备份节点,所以 Redis 集群至少需要 6 台服务器。

集群的总结

优势

  1. 无中心架构

  2. 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布

    解耦数据和节点之间的关系,简化了扩容和收缩难度;

    节点自身维护槽的映射关系,不需要客户端代理服务维护槽分区元数据

    支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景.

  3. 可扩展性,可线性扩展到 1000 个节点(官方推荐不超过 1000 个)节点可动态添加或删除

  4. 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升

  5. 降低运维成本,提高系统的扩展性和可用性

不足

  1. Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性

  2. 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout)被判断下线,这种 failover 是没有必要的

  3. 数据通过异步复制,不保证数据的强一致性

  4. 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容 易出现相互影响的情况。

发布于: 2021 年 05 月 22 日阅读数: 1054
用户头像

我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入

🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝

评论

发布
暂无评论
🚄【Redis 干货领域】帮你完全搞定Cluster原理(架构篇)