浅谈 Redis 集群架构与工作机制

163 阅读10分钟

引言

我们日常写后端的时候,或多或少都会接触到redis,因为它对缓存处理,降低数据库的qps有着重要的作用,但同时也需要防止雪崩和穿透。

由于分布式服务架构已经十分普遍,因此我们需要对 Redis 集群架构有一定的了解。下面就让我们一起来回顾一下Redis的相关知识以及它的集群架构。

Redis 是一个键值类型的内存数据库,广泛应用于缓存、消息队列、会话存储等场景。然而,当数据量或并发请求超出单台 Redis 服务器的处理能力时,我们需要将 Redis 扩展为一个分布式系统。Redis 集群(Redis Cluster)便是为了解决这一需求而设计的,它不仅能够实现数据的自动分片,还提供了高可用性,确保在节点故障时依然可以继续服务。

一、Redis 集群架构

image.png

架构核心组件

  1. 客户端

    客户端是与 Redis 集群交互的应用程序或服务。客户端可以直接连接到 Redis 集群中的某个节点,发送读写请求。客户端需要支持 Redis 集群模式,这样才能正确处理请求的路由和重定向。

  2. Redis Cluster Proxy

    Redis Cluster Proxy 是一个可选的组件,通常用于负载均衡。它位于客户端和 Redis 集群之间,帮助客户端自动处理请求的路由。

    Proxy 可以减轻客户端处理复杂路由逻辑的负担,但这并非 Redis 集群的必需组件。

  3. Redis 集群(Cluster模式)

    Redis 集群由多个主节点(Master Node)和从节点(Slave Node)组成。每个主节点负责一定范围的哈希槽(Slots),即存储一定范围的键。

    例如,在上图中,主节点 A 负责槽位 0 到 5000,主节点 B 负责槽位 5001 到 10000,而主节点 C 负责槽位 10001 到 15000,而其他主节点(扩展) 负责槽位 15001 到 16383。集群中的所有主节点共同覆盖整个 16384 个槽位。

  4. 主节点

    主节点是 Redis 集群中负责处理实际读写请求的节点。每个主节点负责特定范围的槽位,并存储这些槽位内的键值数据。

    在 Redis 集群中,主节点之间没有单点控制,它们共同维护集群的健康状态和数据一致性。

  5. 从节点

    每个主节点都配有一个或多个从节点。从节点是主节点的副本,用于数据备份和故障转移。当主节点发生故障时,从节点会自动升级为新的主节点,接管其职责。

    从节点通常不处理写请求,但可以用于读取操作,以分担主节点的负载。

  6. 集群拓扑

    Redis 集群是一个无中心化的架构,每个节点都持有集群的完整拓扑信息。节点之间通过 Gossip 协议进行通信,定期交换状态信息以保持一致性。

    如果某个主节点故障,集群会自动进行重新选举和槽位重分配,以确保服务的持续可用性。

二、Redis 集群的工作流程

Redis 集群架构通过主从复制槽位分片去中心化设计等机制,实现了高可用性、可扩展性和高性能。

image.png

上图是 Redis 集群的读与写流程,此过程中会涉及到Redis 集群的核心概念,如数据分片节点通信故障处理数据一致性客户端交互等。

1. 数据分片

数据分片主要包括槽位分配、槽位到节点的映射以及数据存储和读取。

a) 槽位分配:Redis 集群将整个键空间分为 16384 个槽位(slots)。每个键根据其哈希值通过 CRC16 算法计算得到一个槽位编号,这个编号决定了键值对在集群中的存储位置。

b) 槽位到节点的映射:集群中的每个主节点负责一部分槽位。当客户端发送请求时,Redis 集群根据键的哈希值确定其对应的槽位,并将请求路由到负责该槽位的主节点进行处理。

c) 数据存储和读取:当数据存储时,客户端会将键值对发送到正确的主节点,主节点根据槽位信息存储数据。读取数据时,客户端会向负责该槽位的主节点发送请求并获取数据。

2. 节点通信

节点之间的通信主要通过Gossip 协议、槽位信息的同步和PING/PONG 消息实现通信。

a.Gossip 协议:Redis 集群中的节点使用 Gossip 协议进行通信。节点会定期向集群中的其他节点发送状态信息,并接收来自其他节点的状态更新。这种方式确保了集群中的每个节点都能实时了解其他节点的健康状况。

b.槽位信息的同步:在节点之间进行通信时,槽位的分布信息会在节点之间同步。这确保了每个节点都拥有整个集群的槽位分布情况,能够在收到客户端请求时正确路由。

c.PING/PONG 消息:节点之间还通过 PING 和 PONG 消息确认彼此的连接状态。如果某个节点在一段时间内未能响应 PING 消息,其他节点将其标记为下线(fail),并开始故障处理流程。

3. 故障处理

在 Redis 集群中,存在自动化故障处理机制。当节点发生故障时,集群会自动重组,选举新的主节点并重新分配槽位。

故障检测:节点之间使用 Gossip 协议定期交换状态信息,如果某个节点未能及时响应,将被标记为故障。

故障转移:如果主节点发生故障,集群会自动选举一个从节点作为新的主节点,并将其数据槽位重新分配。

4. 数据一致性

如何保证各节点之间的数据一致性?比如一个节点新增了一个新key,如何同步到其他节点?

这里主要有两个机制去保证数据的一致性,分别为异步复制一致性模型

异步复制:Redis 集群的主从复制是异步的。当主节点接收到写请求时,会立即返回给客户端成功响应,同时将数据异步复制到从节点。这种机制提高了系统的性能,但也可能导致在主节点故障时出现数据丢失的情况。

一致性模型:由于 Redis 集群使用的是异步复制,在主节点失效和故障转移过程中,可能会有少量的数据未能同步到从节点。这种情况下,新主节点可能会丢失这部分数据,但集群会在短时间内恢复一致状态,保证系统的最终一致性。

5. 客户端交互

使用 Redis 集群时,客户端必须支持集群模式。常见的 Redis 客户端库如 redis-pyJedis 等,都提供了集群支持。

这里与客户端的交互主要有两种,一是请求重定向,二是集群感知。

请求重定向:如果客户端发送的请求被路由到错误的节点,Redis 集群会返回一个 MOVED 错误,指示客户端重定向请求到正确的节点。大多数 Redis 客户端库都能够自动处理这种重定向。

集群感知:使用 Redis 集群时,客户端必须支持集群模式。集群感知客户端能够在初次连接时获取整个集群的槽位分布信息,并在后续请求中自动选择正确的节点进行通信。

三、Redis 集群的优势与劣势

Redis 集群是 Redis 的分布式实现,提供了数据自动分片和高可用性,能够有效地应对大规模数据和高并发场景。然而,它毕竟是一个健值类型的内存数据管理架构,面对复杂的分布式架构,也会存在一定的局限。它的优劣势如下:

优势

1)水平扩展:通过自动分片,Redis 集群能够轻松扩展存储和处理能力;

2)高可用性:主从复制和自动故障转移机制确保了集群的高可用性;

3)无中心化:避免了单点故障,提升了系统的可靠性;

劣势

1)数据一致性:由于采用异步复制,可能会在故障转移时出现数据丢失的情况;

2)复杂性:相比单节点 Redis,集群的部署和维护更为复杂,可能需要更多的运维经验。

四、如何部署 Redis 集群

一个从头开始部署 Redis 集群的完整示例,假设你在本地或远程服务器上进行部署。

步骤 1:准备环境

  1. 安装 Redis

    首先,需要在每个节点(服务器)上安装 Redis。如果你还没有安装,可以通过以下命令安装 Redis:

    sudo apt-get update
    sudo apt-get install redis-server
    
  2. 检查 Redis 版本

    确保你安装的 Redis 版本支持集群功能(通常 3.0 及以上版本)。

    redis-server --version
    
  3. 准备节点

    假设你准备了 6 个 Redis 实例(3 个主节点和 3 个从节点),分别运行在 6 个不同的端口上,如 7000, 7001, 7002, 7003, 7004, 7005。你可以在一台服务器上启动多个 Redis 实例,也可以在不同服务器上部署 Redis 实例。

步骤 2:配置 Redis 实例

  1. 复制 Redis 配置文件

    创建多个 Redis 配置文件,每个实例对应一个配置文件。以 redis.conf 为模板,复制并命名为 redis-7000.conf, redis-7001.conf, 等等。

    cp /etc/redis/redis.conf redis-7000.conf
    cp /etc/redis/redis.conf redis-7001.conf
    cp /etc/redis/redis.conf redis-7002.conf
    cp /etc/redis/redis.conf redis-7003.conf
    cp /etc/redis/redis.conf redis-7004.conf
    cp /etc/redis/redis.conf redis-7005.conf
    
  2. 修改配置文件

    对每个配置文件进行如下修改:

    port 7000  # 对应的端口号,其他文件修改为 7001, 7002, ...
    cluster-enabled yes  # 启用集群模式
    cluster-config-file nodes-7000.conf  # 集群节点配置文件,其他文件分别为 nodes-7001.conf, ...
    cluster-node-timeout 5000  # 节点超时时间
    appendonly yes  # 开启 AOF 持久化
    

    继续修改每个配置文件中的 bind 参数,如果实例运行在不同的 IP 上,请指定实际的 IP 地址。

步骤 3:启动 Redis 实例

启动每个 Redis 实例:

redis-server redis-7000.conf
redis-server redis-7001.conf
redis-server redis-7002.conf
redis-server redis-7003.conf
redis-server redis-7004.conf
redis-server redis-7005.conf

你可以通过以下命令检查每个实例是否启动成功:

ps aux | grep redis

步骤 4:创建 Redis 集群

  1. 使用 redis-cli 创建集群

使用 redis-cli 工具连接到任意一个 Redis 实例,并创建集群。以下命令会将端口 7000 到 7005 的实例配置为一个集群:

   redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1

--cluster-replicas 1 表示每个主节点会有一个从节点。执行该命令后,系统会提示你确认槽位分配,输入 yes 确认。

  1. 验证集群状态

连接到任意一个节点,使用以下命令验证集群的状态:

    redis-cli -c -p 7000 cluster info

你应该会看到集群状态为 cluster_state:ok,表示集群已经成功启动。

步骤 5:测试集群

  1. 插入数据

连接到集群中的某个节点,并插入一些键值对:

   redis-cli -c -p 7000
   set foo bar
   get foo
  1. 检查数据分布

使用 cluster slots 命令可以查看槽位分布情况:

  redis-cli -c -p 7000 cluster slots

你会看到键值被分布到不同的节点上。

至此,Redis 集群已部署完成,当然,实际应用中会有更多复杂的配置。