vlambda博客
学习文章列表

聊聊 Redis 分片集群

今天继续来聊一聊 Redis 里的分片集群。

实际上分片集群原理很大程度上依赖于和。而分片集群相比这两种来说,它更多的是为了解决写的扩展能力。

1.负载均衡

当我们尝试搭建一台具有三主三从的分片集群时,首先我们能想到的是,三台主节点在做存储的时候,该选择哪一台呢?我们可以想象一下nginx的负载策略,有

  • 随机轮询
  • 加权轮询
  • 最小连接数
  • hash
  • 等等

但由于分片集群的特殊性,因为我们要存储数据。假设你选择随机抑或是最小连接数,那这个特性会实时变化,显然当数据存储进去后,下一次在获取就可能获取不到了。所以,这几种不可取的。而 hash 策略不错,我们常用于数据分表。而 hash 策略又可分为

  • 简单哈希
  • 一致性哈希
  • 哈希槽

1.1 简单哈希

Hash(key) % 3

这个算法很简单,对master的数量做取模运算。但缺点很明显,当某个节点增加或移除后,路由方式发生变化,会造成大面积的数据无法访问到。而我们常说的缓存击穿正是这个问题。

1.2 一致性哈希

很显然,一致性哈希正是为了解决简单哈希的问题。首先,它不在直接对master数量做取模运算了,而是对2^32次方做取模,值的范围在[0,2^32-1]。一致性哈希将范围抽象成一个圆环,使用哈希算法计算出来的哈希值会落在圆环上。然后我们的master实例也会分布在圆环上,在圆环上沿着顺时针方向查找,这样便完成了一次的 key 的分配。如下图假设现在有 A、B、C 三个实例按照如图所示的位置分布在圆环上,此时计算出来的 hash 值,取模之后位置落在了位置D,那么我们按照顺时针的顺序,就能够找到我们这个 key 应该分配的实例B。同理如果我们计算出来位置在E,那么对应选择的实例就是A。即使这个时候实例 B 挂了,也不会影响到实例 A 和 C 的数据,也就是它的影响面就比较小,只有 B 节点会受影响。但是一致性哈希也有个问题,就是数据倾斜和数据不平衡问题。如下图此时数据落在节点 A 上的概率明显是大于其他两个节点的,其次落在节点 C 上的概率最小。这样一来会导致整个集群的数据存储不平衡,AB 节点压力较大,而 C 节点资源利用不充分。为了解决这个问题,一致性哈希算法引入了「虚拟节点机制」。

1.3 哈希槽

哈希槽算法与一致性哈希算法类似,分片集群将自己分成了 16384 个Slot(槽位)。通过 CRC16 算法计算出来的哈希值会跟 16384 取模,取模之后得到的值就是对应的槽位,然后每个节点都会负责处理一部分的槽位,就像下表这样。

节点 处理槽位
A 0 - 5000
B 5001 - 10000
C 10001 - 16383

我们知道,Redis最终是采用哈希槽的方案。在我们了解了相应的负载算法后,我们可以在继续来聊下Redis如何进行自动扩缩容?

2.自动扩缩容

为什么要说这个扩缩容呢?我们都知道,分片集群扩展的是写的能力,而根据哈希槽算法,每个节点会负责一部分的槽位,当节点上线,槽位必须要重新分配,不然就无法体现写的扩展能力了。而当节点下线,槽位也是必须要重新分配的,不然原先节点负责的槽位就没有节点负责了。而Redis是通过 reshard 来实现的。这个是Redis内部自己管理的,对于使用者来说并不需要去关注。

3.高可用及故障转移

这和模式相似,就不多做介绍了。

4.集群间节点通信机制

集群元数据的维护,一般有

  • 集中式
  • 分散式

而我们知道Redis使用的分散式的模式。

4.1 集中式

这个模式顾名思义,就是所有元数据的维护交由某个节点。优点是对于读取和维护变得简单,缺点就是某个节点压力较大。

4.2 分散式

Redis使用了名为「gossip」的协议。中文解释为流言,八卦。这个模式同样的,也是顾名思义,每个节点都维护着一份节点信息,而他们之间要不断的同步,以此来达到一致的状态。优缺点和集中式的正好相反。

4.3 简单了解下 gossip 协议

协议间通信的端口是 10000 + Redis 节点通信端口,例如 6379。那它又包含了那些消息类型呢?如下表

消息类型 消息内容
MEET 给某个节点发送MEET消息,请求接收消息的节点加入到集群中
PING 每隔一秒钟,选择5个最久没有通信的节点,发送PING消息,检测对应的节点是否在线;同时还有一种策略是,如果某个节点的通信延迟大于了cluster-node-time的值的一半,就会立即给该节点发送PING消息,避免数据交换延迟过久
PONG 当节点接收到MEET或者PING消息之后,会反馈一个PONG消息给发送方,代表自己收到了MEET或者PING消息。同时,节点也可以主动的通过PONG消息向集群中广播自己的信息,让其他节点获取到自己最新的属性,就像完成了故障转移之后新的master向集群发送PONG消息一样
FAIL 用于广播自己的对某个节点的宕机判断,假设当前节点对A节点判断为宕机,就会立即向Redis Cluster广播自己对于A节点的判断,所有收到消息的节点就会对A节点做标记
PUBLISH 用于向指定的Channel发送消息,某个节点收到PUBLISH消息之后会直接在集群内广播,这样一来,客户端无论连接到任何节点都能够订阅这个Channel

通过这个表格我们可以知道,这个协议对 Redis 来说有这么几点好处

  • 易扩展
    • 节点非常容易增加或减少
  • 高度容错
    • 因为元数据是分散,挂掉了某几台后,元数据依然是完整的
  • 健壮性也比较好

本篇分享先到这里,欢迎关注。

如有收获,点个在看,诚挚感谢