侧边栏壁纸
  • 累计撰写 247 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

通过 Redis Cluster 水平扩展 Redis 构建分布式 Redis 集群

kaixindeken
2021-08-12 / 0 评论 / 0 点赞 / 116 阅读 / 2,441 字

引子

随着业务规模的增长,存放在内存中的 Redis 数据越来越多,最终肯定会面临现有内存资源不足以存放 Redis 数据的问题,如果数据规模很小,比如几个 G,可以轻松通过升级 Redis 实例内存来解决,比如原来是 4GB 内存,则将其扩展到 8 GB或者更高,但是如果需要存放几十几百 GB 的数据呢?

显然,一味增加内存并不是终极解决方案,还是存在物理上限问题,这也是是典型的单体化应用思维,那终极解决方案是什么呢?如果你之前有过数据库架构经验,应该很容易联想到在数据库中,可以通过分库分表将数据分散到多个实例来解决资源的物理上限及系统性能问题,那 Redis 有没有类似的解决方案呢?

当然有,这就是学院君今天要给大家介绍的,Redis 官方提供的 Redis 集群水平扩展解决方案 —— Redis Cluster(与之相对的,可以将增加内存的扩展方式称之为垂直扩展),通过 Redis Cluster,我们可以把 Redis 数据分散存储到多个 Redis 节点。显然,这种水平扩展的方式理论上可以支持无限扩展 —— 当现有节点数量不够,新增机器资源即可。

这种思维是典型的分布式应用思维,和我们水平扩展服务器集群来应对更多 Web 请求,水平扩展数据库集群来应对更大容量数据、提高数据库集群吞吐量的思路如出一辙。

接下来,我们就来看看 Redis Cluster 集群的底层原理以及如何搭建这样的分布式 Redis 集群。

Redis Cluster 底层工作原理

和常见的分布式集群一样,一个典型的 Redis Cluster 集群由三个 Redis 节点组成,每个节点存储一部分 Redis 数据:

1.png

这里必须要要有一个划分指定数据到指定节点的算法,否则,就无法定位数据存放在哪个节点了。

Redis Cluster 会将所有数据划分到 16384 槽位(0-16383),每个节点存放一部分槽位,我们可以在初始化集群时指定每个节点存放的槽位信息(根据不同节点资源配置可以为不同节点分配不同的槽位数量,如果都一样,则使用默认的平均分配规则即可)。

当我们需要存储 Redis 数据时,对应的槽位定位算法是:

slot = CRC16(key) % 16384

其中 key 代表的是待存储数据的键,我们通过 CRC16 算法计算 key 的哈希值,然后通过对 16384 取模获取对应的槽位。显然,这个槽位是落在 0-16383 这个区间的。

取模和求余运行在被除数和除数符号相同时结果一样,在符号不同时,不同语言实现不同,可以简单理解为取模是计算机概念,而求余是数学概念。

这样一来,根据这个槽位所在节点,就可以唯一确定地将 Redis 数据存放到对应节点的槽位了。

以上是服务端分配槽位和存储数据的逻辑,那客户端又是如何得知与哪个 Redis 服务端建立连接的呢?

在 Redis 客户端与 Redis Cluster 集群建立连接时,也会获得一份集群的槽位配置信息,这样,当客户端需要获取某个 key 时,根据其槽位所在节点,就可以直接与目标 Redis 服务端建立连接了。

为了保证高性能,客户端会将获取到的配置信息缓存下来,这样一来,如果服务端配置发生变更,比如新增节点和删除节点,或者重新分布了槽位信息,就会出现客户端与服务端槽位配置不一致的情况。

当客户端向一个错误的目标节点发送请求时,该节点会向客户端发送一个携带正确目标节点地址的重定向 MOVED 指令,这样一来,客户端就可以拿着这个新的节点去获取键值信息,同时还会更新本地缓存的槽位配置,以便后续请求可以使用新的配置,从而达到自我纠正的效果。

基于 Docker Compose 构建 Redis Cluster 集群

了解了 Redis Cluster 集群大致的工作原理后,我们尝试来搭建一个 Redis Cluster 集群,还是以 Laradock 环境为例,它已经为我们提供了一个示例配置,这个示例是基于 grokzen/redis-cluster 这个 Docker 镜像的。

我们在 docker-compose.yml 中为 redis-cluster 新增几个环境变量:

### Redis Cluster ##########################################
    redis-cluster:
      build: ./redis-cluster
      environment: 
        - MASTERS=3
        - SLAVES_PER_MASTER=2
        - SENTINEL=true
      ports:
        - "7000-7008:7000-7008"
      networks:
        - backend

表示这个 Redis Cluster 集群由三个主库节点(默认值),每个主库有两个从库(默认是1个),并且启用了哨兵机制(默认不启用),另外,将 ports 接口映射做了调整,因为现在对外可以有 9 个 Redis 节点,原来的配置不够用了。

接下来,运行如下指令即可启动这个 Redis Cluster 集群:

docker-compose up -d redis-cluster

启动成功后,通过 docker-compose ps 查看容器进程:

1.png

其中端口号为 500X 的是哨兵节点,端口号为 700X 的是 Redis 主从库节点,你可以进入 redis-cluster 容器内部查看明细:

1.png

1.png

整体架构如下所示:

1.png

由于 grokzen/redis-cluster 镜像的 Sentinel 配置文件设置所有主库都位于一台实例,所以现在只有三个哨兵节点,你可以修改这些配置信息,让这三个 Redis 主从集群部署到不同的节点,然后为每个主从集群配置独立的哨兵集群,不过这样一样,整体架构为更加复杂,我们这里只是本地演示,不再单独介绍了。

我们可以在任意节点通过 cluster nodes 获取槽位与 Redis 节点(主库节点,从库和对应主库槽位一致)的对应关系:

1.png

1.png

这里,可以看到,默认槽位分布是均匀分布的。

要获取某个键对应的槽位信息,可以通过 cluster keyslot 命令:

1.png

这样就可以判定它位于当前节点上。

如果我们试图在其他主从集群获取这个键,就会返回重定向指令:

1.png

要求我们去端口号为 7000 的 Redis 节点获取这个键值对。

0

评论区