Redis 架构深入:主从复制、哨兵到集群

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
传统型负载均衡 CLB,每月750个小时 15LCU
简介: 大家好,我是小康,今天我们来聊下 Redis 的几种架构模式,包括主从复制、哨兵和集群模式。

大家好,我是小康,今天我们来聊下 Redis 的几种架构模式,包括主从复制、哨兵和集群模式

前言:

设想一下,你的咖啡馆在城市中太受欢迎,导致每天都人满为患。为了缓解这种压力,你决定在其他地方开设分店,这样顾客就可以在附近的分店享受咖啡,而不必涌向一个地方,这就好比 Redis 的主从复制,让数据备份并允许多个地方进行读取。

但这还不够,因为你需要确保当主要的咖啡馆遇到问题时,例如突然断电,有其他分店能够迅速接手,成为新的主要店铺,继续为顾客提供服务。这就像 Redis 的哨兵系统,它会自动检测故障并进行转移,确保服务始终在线。

最后,随着咖啡馆连锁店的增长,每家店都开始独立运作,甚至可能有自己的特色饮品和优惠活动,同时仍然保持整体的协调和一致性。这就是 Redis 集群的工作方式,它将数据分片到不同的节点,每个节点都可以独立处理请求,但都是整个系统的一部分。

现在,让我们深入探讨 Redis 如何通过主从复制、哨兵和集群来确保它的强大性能和高可靠性。

主从复制

Redis 主从复制.png

主从复制基本概念

在日常工作中,我们都知道团队合作的重要性。就像团队中有领导者和执行者,Redis 也采用了一种类似的策略 —主从复制,使其能够更有效地处理数据。

什么是主从复制?

主从复制是一种允许多个 Redis 服务器(节点)协同工作的策略。其中,一个服务器作为“主节点”,负责接受所有写操作。其他服务器则作为“从节点”,复制并存储主节点的数据。这样,从节点可以为读操作服务,从而分散主节点读取压力。

简单图示:

redis主从复制.drawio (3).drawio (2).png

主从复制的工作原理

Redis 的主从复制功能使得一个或多个从节点可以复制一个主节点的全部数据。主节点负责进行写操作,而从节点则可以用来进行读操作,从而分担读的负载。

主从复制流程图

主从复制流程图.png

步骤说明:

从节点首先执行 REPLICAOF ip port 命令,准备复制主节点的数据。

第一步:连接与同步请求

  • 从节点与主节点建立 TCP 连接,并正式请求同步数据,发送 PSYNC ? -1。

  • 主节点接收到请求后,向从节点回复 FULLRESYNC runid offset ,做好开始传输 RDB 数据的准备。

第二步: RDB 文件同步

  • 主节点执行 bgsave 命令,生成当前数据的快照—也就是 RDB 文件。

  • 主节点随后将这个 RDB 文件传输给从节点。

  • 从节点收到 RDB 文件后,开始加载到自己的数据库中。

第三步 : 持续命令同步

  • 之后,每当主节点有新的写命令,它都会立即传输给从节点并同时将新命令追加到复制缓冲区中。

  • 从节点持续接收并执行这些命令,确保与主节点的数据始终保持同步。

断线重连与部分同步

在实际应用中,因为网络不稳定、系统资源限制等因素,Redis 主从节点间的连接有时会遭遇中断。为了应对这些突发情况并确保数据同步不被中断,Redis 设计了一套智能的同步恢复机制: 部分同步

断线重连与部分同步流程图

redis 主从复制断线重连 (6).drawio.png

步骤说明:

  1. 🔌 [连接断开]

    🚫 主从节点的连接中断。

  2. 🔄 [重连尝试]

    📡 从节点尝试重新连接主节点。

  3. 💬 [部分同步请求]

    📩 从节点发送 PSYNC 带有上次的运行 ID 和数据偏移量。

  4. 🤔 [主节点决策数据同步方式]

    ✅ 如果条件满足:回复 CONTINUE,表示进行部分数据同步。

    ❌ 否则:回复 FULLRESYNC,表示进行全量数据同步。

  5. 🏁 [执行命令]

    🛠 从节点执行接收到的新命令,确保数据与主节点一致。

这里重点讲解下部分数据同步的过程:

当从节点重新连接到主节点时,它会发送之前保存的主节点运行 ID 和自己的复制偏移量给主节点。

主节点在验证接收到的运行 ID 与自己的相符后,会进一步检查其复制缓冲区。如果缓冲区包含从节点缺失的所有命令,主节点仅发送这些缺失命令。否则,将进行全量同步。

主从复制配置详解

关于 Redis 主从复制的配置项,只需在 redis.conf 配置文件中搜索 replication 即可定位到。

1.连接 & 身份验证:

  • replicaof masterip masterport : 设定从节点连接到的主节点地址和端口。
    • masteruser username : 设定连接到主节点的用户名。
    • masterauth master-password : 设定连接到主节点的密码。

2.数据服务策略

  • replica-serve-stale-data : 当与主节点断开连接时,从节点应如何响应客户端的数据请求,默认值 yes。

    yes: 提供当前拥有的数据(即使可能过时)

    no: 不提供任何数据,返回“正在与主节点同步”的错误。

  • replica-read-only : 是否设置从节点为只读,默认值 yes。

    yes : 只读, no : 可写。

3.同步策略

  • repl-diskless-sync: 选择 RDB 数据同步方式,默认值 no。

    yes : 直接通过网络同步 RDB 数据。

    no : 先将 RDB 数据写入磁盘,然后再同步。

  • repl-diskless-sync-delay timeout : 当启用无磁盘同步时(repl-diskless-sync 设置为 yes),主节点在开始数据同步前的等待时间(秒),以便集合更多的从节点连接请求,从而进行高效的一次性同步。

  • repl-diskless-load disabled : 从节点如何加载 RDB 数据,默认值 disabled。

    disabled : 从节点会先把接收到的RDB文件保存到磁盘,然后从磁盘读取这个文件。

    on-empty-db : 从节点直接从网络加载 RDB 数据,但只有当它的数据库是空的时。

    swapdb : 从节点会同时保存旧数据和从主节点接收的新数据在内存中,不通过磁盘。但需要注意,这需要很多内存。

4.PING & 超时

  • repl-ping-replica-period 10 : 从节点向主节点发送 PING 的频率,用于确认主从节点的连接状态。默认值 10s。

  • repl-timeout 60 : 主节点和从节点之间数据传输的超时时间(以秒为单位)。默认值 60s。

  • repl-disable-tcp-nodelay no : 是否禁用 TCP_NODELAY,默认值 no。

    yes: 禁用, no: 启用。

5.积压策略

  • repl-backlog-size 1mb : 设置复制积压缓冲区大小,默认值 1MB。

  • repl-backlog-ttl 3600 : 复制积压的生存时间,默认值 1h。

6.从节点属性

  • replica-priority 100 : 设置从节点的优先级,使用哨兵 Sentinel 进行故障转移时,就用到了这个配置。

    数值越小,优先级越高。设置为 0 时,该从节点不会被提升为主节点。默认值 100。

  • replica-announced yes : 该配置决定是否让其他的 Redis 工具(如哨兵 Sentinel,Redis 集群)知道这个从节点的存在。默认值 yes。

    yes : 当设置为"yes",这意味着其他 Redis工具可以看到和识别这个从节点。该节点可以进行正常的故障转移。

    no: 当设置为"no",这意味着该从节点对其他 Redis 工具是不可见的,它会"隐藏"自己。这在某些特定的部署或安全场景下可能是有用的,比如你不希望某些从节点被外部发现或被用于故障转移。

  • replica-announce-ipreplica-announce-port : 用于指定 Redis 实例应当通告给其他节点的IP地址和端口。

    场景:考虑一个使用 Docker 部署的 Redis 实例。默认情况下,该Redis 实例的IP地址为容器的内部私有地址,可能不适合外部访问。

    replica-announce-ip : 在这种设置下,你需要将此配置项设为外部网络可访问的地址,如宿主机的IP,以确保其他 Redis 节点或客户端可以正常连接。

    replica-announce-port : 若你的容器内部 Redis 实例使用的是标准端口 6379,但在宿主机上通过 7000 端口映射为外部访问,那么你应该将此配置项设置为 7000。

    这样的配置确保无论是 Redis 节点还是客户端,都能使用正确的 IP 和端口与容器内的 Redis 实例进行通信。

7.写操作确认参数

  • min-replicas-to-write : 它定义了主节点在执行写操作时,至少需要多少个从节点确认已经接收了这个写操作。这样,主节点才会回应客户端写操作是成功的。

  • min-replicas-max-lag : 它定义了从节点与主节点之间的最大允许数据同步延迟(以秒为单位)。如果某个从节点的同步延迟超过这个设定的时间,那么该从节点的确认将不会被计入 min-replicas-to-write 所需的确认数量。

主从复制使用场景

1.负载均衡

背景 :大规模的应用可能会产生大量的读请求,这些读请求如果都落到一个服务器上,可能会导致性能瓶颈,进而影响用户体验。

解决方法

  • 使用 Redis 的主从复制,可以将读请求负载均衡到多个从节点上,而主节点主要负责处理写请求

  • 这种方式可以有效地分散请求,减轻主节点的负担,确保系统的流畅运行。

2.数据冗余与备份

背景:为了确保数据安全性,我们需要在多个地方存储数据的备份。

解决方法

  • 通过主从复制,数据在多个从节点上都有拷贝。
  • 即使主节点遭遇故障,数据仍然安全,因为可以从任何从节点恢复。

3.读写分离

背景:当业务增长导致单一 Redis 实例的读写请求压力增大时,采用主从复制进行读写分离可以确保系统性能稳定并提高可用性。

解决方法

  • 使用主从复制进行读写分离:主节点负责所有写操作,而从节点处理大部分读操作。
  • 这确保了即使主节点遭遇大量写入,读操作的性能仍然保持稳定。

4.故障恢复

背景:当主节点出现故障时,服务可能会中断。

解决方法

  • 通过配置主从复制和搭建哨兵集群,当主节点故障时,一个从节点可以被晋升为新的主节点,这样,服务可以快速恢复,减少停机时间。

主从复制的局限性及其解决策略

前文详细介绍了 Redis 主从复制的基本概念及其工作原理和配置方法。但如同所有技术一样,主从复制也有其局限性。为了更全面地应用 Redis,接下来我们将探讨其局限性及其解决策略。

  1. 单点故障(SPOF)
  • 局限性:如果主节点发生故障,整个系统的写能力会受到影响,直到手动干预恢复或切换到从节点。
  • 解决策略:部署Redis哨兵(Sentinel)系统来实现故障自动检测和主节点的自动故障转移,减少系统恢复时间,提高可用性。
  1. 数据一致性问题
  • 局限性:在 Redis 主从复制中,数据更新首先在主节点上进行,然后异步复制到从节点。这种异步复制是滞后的,从节点的数据状态可能落后于主节点,导致读操作可能读取到过时的数据。
  • 解决策略

    a. 对于读操作:为了减少读取过时数据的风险,可以人为地在从节点上引入延迟回应读请求的机制。这意味着从节点会等待一定的时间(足以让大部分的写操作被复制)后再回应读请求。这种策略并不是 Redis直接提供的功能,而是需要通过应用逻辑或中间件来实现。具体做法可能包括在应用程序中增加缓冲逻辑,或者使用代理层来控制对从节点的读请求延迟。

    b.对于写操作:
    使用 WAIT 命令,WAIT 命令是 Redis 提供的一种机制,允许在写操作后等待直到指定数量的从节点确认接收到了这次更新。这样可以在写入数据后立即强制数据同步,减少数据不一致的风险。具体实现就是在执行关键写操作后,通过调用 WAIT 命令并指定要等待的从节点数量和超时时间,可以确保这些更新在继续执行后续操作前被足够多的从节点接收,从而提高整体数据一致性。

    WAIT 命令格式:WAIT <numslaves> <timeout>
    <numslaves>:等待确认写操作的从节点数量。
    <timeout>:等待的最长时间(毫秒)。如果超时,命令会返回实际确认的从节点数量,即使这个数量小于<numslaves>。
    

3. 写入能力受限

  • 局限性:所有的写操作都必须由主节点处理,限制了系统的整体写入吞吐量。
  • 解决策略:通过搭建Redis集群,分散写入操作到多个主节点,实现写入能力的水平扩展。

Redis 哨兵

通过 Redis 的主从复制机制,我们可以实现数据的冗余备份,负载均衡。但在实际的生产环境中仅仅有主从复制是不够的。当主节点出现故障时,系统如何自动、快速地将一个从节点提升为新的主节点以保证服务的连续性呢?这就引出了我们接下来要讨论的话题—Redis 哨兵 Sentinel

Redis 哨兵.png

哨兵基本概念

什么是哨兵?

Redis 哨兵是一种监控工具,确保你的 Redis 始终可用。想象一下,你的主Redis 节点突然停机了,怎么办?这时,哨兵进入舞台中央!它自动检测问题,并迅速选择一个备用 Redis(从节点)来接管,确保数据服务不中断。

哨兵的配置和部署

哨兵的配置

Redis 哨兵使用的配置文件是 sentinel.conf

这里,我只讲解几个重要的配置项:

# 哨兵的监听端口
port 26379
# yes 代表哨兵以后台的方式启动
daemonize yes
# 设置哨兵的日志文件路径
logfile "/var/log/redis/sentinel.log"

# 为哨兵指定要监控的主节点。
# mymaster,127.0.0.1,6379 分别是主节点的名字,主节点的ip地址,端口
# 数字2 代表的是故障转移的阈值(这意味着,当2个哨兵都同意主节点不可用时,故障转移过程才会开始。)
sentinel monitor mymaster 127.0.0.1 6379 2  

#设置哨兵等待主节点响应的时间,超过30s 代表主节点主观下线。
sentinel down-after-milliseconds mymaster 30000
#当哨兵完成主从切换之后,这个配置决定有多少个从节点可以同时与新的主节点进行数据同步。
sentinel parallel-syncs mymaster 1

哨兵的部署

为了确保更高的可靠性和稳定性,建议在至少三台机器上分别部署哨兵实例以形成一个集群。

分别在三台机器上执行以下命令启动哨兵

redis-sentinel /path/to/sentinel.conf

# /path/to/sentinel.conf   哨兵配置文件的实际路径

哨兵集群交互图

哨兵集群.drawio (1).png

哨兵集群中,关于哨兵与哨兵、哨兵与节点之间的通信,有以下三个核心问题需要我们弄清楚:

  1. 哨兵与主节点之间的连接是如何建立的?
  2. 各个哨兵实例是如何建立连接的?
  3. 哨兵又是如何与从节点进行连接的?

第一个问题:

sentinel.conf 中提供了以下配置项:

sentinel monitor 主节点名字 主节点ip 主节点端口  quorum

该配置项提供了主节点的 ip 和 端口, 哨兵通过该地址就可以与主节点建立连接,从而监控它。

第二个问题:

哨兵之间的互相发现和连接主要基于 Redis 的发布与订阅机制:

当哨兵启动并监控主节点时,它会订阅主节点的 __sentinel__:hello 频道,并且也会在 __sentinel__:hello 频道上定期发布自己的信息(如IP和端口)。这样各个哨兵就可以实时获取到对方的 IP 地址和 端口信息。

通过观察上面的交互图,我们可以看到哨兵 B 会先订阅主节点频道 __sentinel__:hello,哨兵 A 、C 会定期在该频道上发布自己的 IP 地址和端口信息,因此,哨兵 B 就会实时获取到 A、C 的地址端口信息,从而与它们建立连接并通信。

第三个问题:
哨兵是通过定时发送 INFO Replication 命令给主节点,来获取所有从节点的 IP 地址和端口信息的。这样,哨兵就可以分别和各个从节点建立连接并进行通信。

INFO Replication 的响应:

> INFO Replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.1,port=6381,state=online,offset=1249761,lag=0
slave1:ip=192.168.1.2,port=6380,state=online,offset=1249761,lag=1
...

# slave0、slave1、slaveXX 等代表的是各个从节点的信息。

哨兵的工作原理

我们都体验过这样的时刻:正当电视剧来到精彩的高潮,突然电视信号断开。如果此时,系统自动帮你切换到一个备用频道,继续播放剧情,你的体验会不会好很多?这正是 Redis 哨兵所提供的功能。

在数据世界中,当 Redis 主节点遇到问题,哨兵就如同这个智能切换系统,自动选择一个备用节点,确保服务的持续。

这一切都归功于哨兵的三大核心职责:哨兵监控、哨兵 Leader 选举、故障转移与恢复。

接下来,让我们探索这三大功能如何确保 Redis 服务的稳定运行。

哨兵监控

Redis 哨兵监控图示:

redis哨兵 (3).drawio.png

通过上图我们可以看到,哨兵集群是通过定期发送 PING 命令到主节点和从节点,等待它们的 PONG 响应来监控它们的健康状态。如果在设定的时间间隔内(比如 10s,配置项为:down-after-milliseconds)未收到回应,哨兵则认为相应的节点可能出现故障,并将其标记为「主观下线」状态。

上面在理解了「主观下线」 后,你可能会想,一个哨兵的主观判断足够可靠吗?实际上,为了避免单点判断误差,哨兵系统一般以集群的方式部署,并且采取一种更集体、更“客观”的判断方式来判定主节点处于下线状态。

更“客观”的判断方式

当一个哨兵认为某节点「主观下线」 后,它会询问其他哨兵是否也觉得该节点不可用。只有当超过半数的哨兵都认为该节点下线时,节点状态才会被标记为「客观下线」 。这种方式确保了系统对主节点状态的判断更为准确和可靠。一旦主节点被哨兵系统判定为「客观下线」 状态后,哨兵系统才会进行后续步骤(哨兵 Leader 选举、故障转移)。

主节点客观下线图解:

redis 哨兵客观下线.png

主节点客观下线图解步骤说明

1. 哨兵健康检查

哨兵 A 尝试 PING 主节点,等待 PONG 响应。

2. 主观下线判断

如果哨兵 A 未收到响应,它将主节点标为 "主观下线"。

3. 询问其他哨兵

哨兵 A 向其他哨兵(如哨兵B、C)广播主节点主观下线的消息,消息格式为:+sdown master 主节点ip 主节点port

哨兵B & 哨兵C 根据自己的观察,回复是否同意主节点主观下线。

如果同意,则会向哨兵 A 回复消息 +sdown master 主节点ip 主节点port

如果不同意,则会选择不回复,哨兵 A 如果长时间收不到哨兵 B、C 的回复,则会判定哨兵 B、C 是不同意主节点主观下线。

4. 主节点客观下线确认

如果大多数哨兵都同意主节点是 "主观下线",那么主节点就被确定为 "客观下线"。随后,哨兵开始选举 Leader ,启动故障转移。

哨兵 Leader 选举

在 Redis 的哨兵模式中,Leader 选举过程是至关重要的。当主节点出现问题时,为确保系统的稳定和高可用,哨兵们需要通过投票机制决定一个哨兵「Leader」来处理这次的故障切换。

但我们得知道,哨兵不是单独工作的,它们通常是以集群形式部署的。在这个集群中,哨兵可以分为两类:普通哨兵和候选哨兵

普通哨兵:它们的主要任务是持续监测Redis主从节点的健康状态。当它们观察到主节点无法正常响应时,它们会标记这个主节点为“主观下线”。

候选哨兵:第一个发现主节点“客观下线”的哨兵就有资格成为候选哨兵,它会尝试启动选举过程,成为领导来处理主节点的故障。

注意: 只要候选哨兵才能够参与选举 Leader

Leader 选举时序图

xxxxxxxxxxxxxxxxxxx.png

Leader 选举步骤详解

1. 请求投票

  • 广播请求:
    候选哨兵会广播一个特定的消息给所有其他哨兵,这个消息可以被理解为:“我认为主节点下线了(客观下线),我可以恢复请投票支持我。

    消息请求格式
    ```

    ip ,port 是主节点的 IP 地址和端口。current-epoch 是请求哨兵的当前纪元,candidate-name 是请求成为领导者的哨兵的名称。

    SENTINEL is-master-down-by-addr

“纪元”(epoch)指的是一个递增的整数,用于标识故障转移尝试的次数和同步哨兵之间的状态。


- 响应与投票:

如果其他哨兵觉得主节点还在线,它们拒绝投票。

如果其他哨兵同样觉得主节点下线并且还未投票,它们支持当前候选哨兵。

**响应消息格式**:

: 是一个整数,1 表示响应者认为主节点下线,0 表示认为主节点在线

: 是 响应者 知道的最大的领导者纪元

: 是响应者认为的当前选举的候选领导者的名字。

- 多个候选哨兵的处理

如果在一个较短的时间窗口内,有多个哨兵都检测到主节点的客观下线并广播成为 Leader 的请求,其他哨兵需要做出选择。在同一纪元中,每个哨兵只能投票一次,通常倾向于支持最早发起请求的候选哨兵。

**2. 统计投票结果**

当候选哨兵收到的投票数达到哨兵总数的大多数时(超过半数),该候选哨兵就已经赢得了这次选举,成为哨兵 Leader,从而进行下一步操作 : **故障转移**。

上面的”半数“由配置项 `sentinel monitor mymaster 127.0.0.1 6379 2` 中的 quorum 参数(最后一个参数)决定。


#### 哨兵 Leader 进行故障转移与恢复

Redis 哨兵 Leader 的核心目标是自动选择一个新的主节点来替代失效的主节点。那么,具体是如何进行的呢?

**第一步:选出新主节点**

在所有的从节点中,哨兵 Leader 会找出一个最适合的节点来替代失效的主节点。选择的准则包括:

- **主从节点断开时间**

  哨兵 Leader 会检查从节点与主节点断开的时间。如果这段时间太长,超出**预设值**,则这个从节点可能数据不新鲜或不稳定,不适合成为新主节点。

  **预设值**:$(down—after-milliseconds * 10) + $milliseconds_since_master_is_in_SDOWN_state$ $

  **公式解释**:主从服务器超时时间的十倍加上从哨兵 Leader 看主服务器不可用的时间。

  **down—after-milliseconds** : 主从服务器超时时间。
  **milliseconds_since_master_is_in_SDOWN_state** : 指的是哨兵 Leader 认为主节点进入主观下线状态后经过的时间。

- **从节点优先级**

  哨兵 Leader 根据主从节点断开时间就可以剔除不合适的从节点,接着将根据从节点的优先级进行过滤。

  每个 Redis 从节点都有一个 replica-priority 配置值,表示节点的优先级。数值越小,优先级越高,其被选作新主节点的机会就越大。Redis 哨兵 Leader 首先会选择优先级比较高的从节点作为新的主节点。

- **已处理的复制偏移量**

  接着哨兵 Leader 会考虑从节点数据的新鲜度。在优先级相同的情况下,数据更新得越频繁的从节点更可能被选为新主节点。

- **运行 ID**

  运行 ID 是每个 Redis 实例启动时生成的唯一标识。当上述条件都相同时,哨兵 Leader 会选择字母顺序中运行 ID 最小的从节点,保证了选择的一致性。

**选定新主节点后,哨兵 Leader 会发送 SLAVEOF no one 命令,将该从节点转化为主节点状态。**

**第二步:从节点连接新主节点**

哨兵 Leader 向每个从节点发出 ` SLAVEOF [新主节点的IP] [新主节点的端口] ` 指令。从节点执行该指令后,会首先与原主节点断开连接,然后与新主节点建立连接,并开始同步数据。

**第三步:将旧的主节点变成从节点**

旧的主节点恢复后,它不再是主节点,因为我们已经有了新的主节点。为了避免数据冲突和保证数据一致性,哨兵需要做以下调整:

**检测旧主节点**:

哨兵会持续地监控所有的 Redis 节点,包括旧的主节点。当检测到旧的主节点重新上线并变得可达时,哨兵会进行下一步操作。

**自动降级和数据同步**:

接着,哨兵 Leader 会自动发送 `SLAVEOF [新主节点的IP] [新主节点的端口] `命令给旧的主节点。这会指示旧的主节点开始从新的主节点复制数据,并且降级为从节点。

**持续监控**:

哨兵集群会继续监控这个旧的主节点,以及其他所有节点,确保整个系统的稳定运行。

**第四步:通知应用程序和客户端**

新主节点准备就绪后,为保证数据的正常存取,我们必须让外部应用和客户端知晓这一变化。此时,哨兵通过 Redis 的**发布/订阅(Pub/Sub)** 机制来发送通知。

**了解一下 Redis 的发布/订阅:**

**发布者** :负责发送消息到特定的频道。

**订阅者** :负责接收某个频道上的消息。

**频道**   :频道是消息传递的媒介。

**事件**   :当在一个特定的频道上发布了一个消息时,这就是一个事件。事件表示了某种特定的状态改变或通知。

发布者(如哨兵)可以将消息发送到特定的频道,而所有订阅了该频道的订阅者都会收到这个消息。

**哨兵如何使用发布/订阅?**

首先,当哨兵 Leader 完成故障转移的主从切换之后,它就会记录下新主节点的 IP 和端口信息,并通知整个哨兵集群。

**消息发布**:

接收到新主节点信息的所有哨兵(无论是 Leader 还是其他哨兵)都会利用 Redis 的发布/订阅功能,在各自的 **+switch-master** 频道上发布这一新信息。

**客户端订阅与接收**:客户端和应用程序只需向哨兵节点订阅该频道,即可实时获取新主节点的IP 地址和端口信息。

**例如**:想要监听主从节点切换的通知,客户端只需向哨兵节点订阅
`SUBSCRIBE +switch-master` 频道 ,这样,一旦有主从节点切换,通过这个订阅,客户端会立即被通知新的主节点 IP 和端口信息。

### 哨兵的优点
- **高可用性**:

当 Redis 主节点(master)出现故障时,哨兵能够自动将某个从节点(slave)晋升为新的主节点,确保 Redis 服务的持续可用性。

- **服务发现**:

客户端和应用程序可通过 Redis 的发布/订阅功能实时查询哨兵,以获取当前主节点的最新地址信息。

## 集群

回想一下,前面我们聊到了 Redis 的主从复制和哨兵,让我们进行一个小回顾:

**Redis的主从复制**:简单来说,就是一个主节点有多个从节点复制其数据,这样做的好处是可以分摊读取的压力。

**哨兵机制**:当主节点出现问题时,哨兵会帮我们自动选出一个新的主节点,确保系统不会因为一个节点的故障而中断。


听起来很完美对吧?但随着我们数据的增长,有一个问题逐渐浮现:如果我们的数据量超出了一个服务器的处理能力怎么办?毕竟单台服务器的资源是有限的。或者说,我想更快地读写数据,单纯的复制够用吗?

为了解决这样的问题,Redis 推出了一个叫做“集群”的模式。简单来说,它就是把数据分散到多个服务器上,让每个服务器只处理一部分数据,从而达到“集体努力”的效果。

听起来很酷吧?但是它是如何工作的?在我们接下来的探讨中,我们会一步步揭开 Redis 集群的神秘面纱,让大家对它有一个更加清晰的认识!

![Redis 集群2.png](https://ucc.alicdn.com/pic/developer-ecology/o5dx7dlccd5jm_d7b638657ea14fdfbd296cbd4de7885f.png)


### Redis 集群基本概念
#### 什么是 Redis 集群?

Redis 集群是一种允许多个 Redis 节点协同工作的技术,它提供了数据分片和故障恢复能力。在 Redis 集群中,数据会被划分为多个分片,每个分片存储在不同的节点上。这不仅使 Redis 能够存储更多的数据,而且,当某个节点出现故障时,其他节点可以迅速地接管它的任务,确保整体服务的连续性。

#### 为什么需要集群?

**数据增长**:随着应用程序和业务的发展,数据量持续增长,可能超出单个 **Redis** 实例的处理能力。

**业务持续性**:为了确保业务的持续运行,即使在硬件故障或其他问题的情况下,我们需要一种更稳健的解决方案。

**性能要求**:随着用户基数的增长和服务的扩展,性能要求也随之提高,集群可以提供更好的读写能力。

### Redis 集群的工作原理

#### 数据分片

**什么是数据分片?**

在 **Redis** 集群中,数据分片是将所有的键分布到多个节点上的方法,从而每个节点只需要持有整个数据集的一部分。

**数据分片的好处**
- 横向扩展:随着数据或请求负载的增长,可以简单地添加更多的节点来分担负载。

- 增加总吞吐量:因为数据和请求被分散到多个节点,每个节点只处理一部分的数据和请求,因此整体吞吐量得到提升。


 **数据如何在集群中分片?**

在 Redis 集群中,数据的分布取决于一个非常核心的概念,那就是**哈希槽**。那么,这一切是如何工作的呢?

**1. 哈希槽:集群的基石**

Redis 集群总共定义了 16384 个哈希槽。

这些哈希槽的职责就是确定一个特定的键应该被存储在哪个节点上。

**2. 如何映射键到哈希槽?**

当你尝试在集群中保存一个键值对时,Redis 并不是随机选择一个哈希槽。

它使用键的名字,对其进行计算,确定应该归属于哪个哈希槽。

**计算公式**:

$slot = CRC16(key) \mod 16384$

CRC16 :计算键的哈希值的函数,slot 是该键所对应的槽号。

这意味着每个键名都会被映射到 0 到 16383 之间的某个哈希槽。

**3. 哈希槽与节点:亲密的伙伴**

当集群设置完毕后,这些哈希槽会被均匀地分配到所有的主节点上。当然也可以通过手动的方式进行分配。

比如,如果你有三个主节点,如果是均匀分配的话,可能第一个节点负责前 5461 个哈希槽,第二个节点负责接下来的 5461 个,第三个节点负责剩余的。

 **4. 客户端请求:如何找到正确的节点?**

- 当客户端想要读取或写入一个键时,它会先计算出这个键应该属于哪个哈希槽。
- 知道了哈希槽,它就能找到对应的节点,并向该节点发送请求。

**为了便于理解,我画一张示意图来解释一下,数据、哈希槽、主节点这三者的映射关系:**

![哈希槽-节点.drawio.png](https://ucc.alicdn.com/pic/developer-ecology/o5dx7dlccd5jm_03023e711a464b2a8b706614b446fb5f.png)




#### 节点的角色:主节点与从节点

在 **Redis** 集群中,数据分片使得数据分布在多个节点上,而这些节点分为**主节点**和**从节点**。它们各自承担不同的职责,共同确保 **Redis** 集群的高可用性和高性能。

**Redis 集群节点交互图:**

![444444444444444444444.png](https://ucc.alicdn.com/pic/developer-ecology/o5dx7dlccd5jm_faee908d91cc4c7ca56c55d32170fdff.png)



**主节点 (Master Node)**

**主节点是 Redis 集群的核心,它们的职责如下:**

- **数据存储**:每个主节点都负责存储整个数据集的一个子集,这取决于它所管理的哈希槽。

- **处理写请求**:所有写入数据的请求(例如:SET, HSET 等)都直接发送到相应的主节点。

- **管理从节点**:主节点负责向其从节点同步数据,确保数据的一致性。

**从节点 (Slave Node)**

从节点为 **Redis** 集群提供额外的数据冗余和读取能力,每个主节点都会有一个或多个从节点,它们的职责如下:

- **数据备份**:从节点复制并存储其对应主节点上的数据,为系统提供一个数据的备份。

- **处理读请求**:为了分担主节点的读负载,客户端可以从从节点读取数据。

- **故障转移**:如果主节点故障,从节点可以被晋升为新的主节点,继续为客户端提供服务。

**如何在节点之间进行数据的读写?**

- **写操作**:客户端首先计算键的哈希槽,确定应该由哪个主节点处理,然后直接向该主节点发送写请求。

- **读操作**:客户端可以选择从主节点或其任何从节点读取数据。当从节点用于读操作时,它可以帮助提高整体的读取性能,特别是在读操作比写操作多的场景下。


#### Redis 集群与 Gossip 协议

在深入了解 Redis 集群中的各个节点的角色之后,你可能会产生这样的疑问:这些节点如何找到彼此,并高效地共享信息和状态呢?当某个节点遇到问题时,其他节点如何迅速得知并做出相应调整?

**答案在于 Redis 集群采用的一个特殊的通信机制—"Gossip" 协议。**

**什么是 Gossip 协议?**

让我们用一个简单的例子来理解。想象一下,你在一个聚会上分享了一个趣事给几位朋友,这些朋友又告诉了其他人。很快,整个聚会上的人都知道了这个趣事。这种信息的传播方式,就是 Gossip 协议的核心思想:**节点间通过“聊天”来共享和扩散信息。**

更专业地说,**Gossip 协议**是一种在分布式系统中进行信息同步和故障检测的轻量级通信机制。节点随机选择其他节点交换信息和状态。

**Redis 集群如何应用 Gossip 协议?**

Redis 集群的 Gossip 协议是基于传统 Gossip 协议之上进行的优化,以满足其特定的需求和特性。

- **节点发现**:

  新加入的节点使用 **Gossip 协议**来识别和握手与集群中的现有节点。进而将自己加入到集群中。

- **状态交换**:

  节点定期使用 **Gossip** 协议与其他节点交换自身及已知的其他节点状态,节点状态包括:节点角色、节点的健康状态,是正常、故障、还是疑似故障。

- **故障检测与通知**:

  如果某节点发现另一节点长时间无响应,它会利用 Gossip 协议迅速传播这一信息,确保整个集群得到通知并采取行动。

- **数据路由**:

  在 Redis 集群中,数据的存储与检索是基于哈希槽的。节点之间需要交换它们各自负责的哈希槽信息,确保客户端请求能准确路由到对应的节点。

**总结**:

**Redis** 集群利用 **Gossip** 协议实现节点间的有效通信,确保节点发现、状态同步、故障检测与通知以及数据路由,从而保障集群的高可用性和数据一致性。

#### Redis 集群的故障转移与恢复

当我们谈论 Redis 集群的鲁棒性时,故障转移与恢复是不可或缺的部分。这确保即使面临节点故障,集群仍然可以正常运行。

**什么是故障转移?**

当主节点遇到问题并无法正常工作时,从节点准备接管并成为新的主节点。这个过程被称为故障转移。


你或许还记得,在哨兵模式中也有故障转移的流程。但 **Redis** 集群采用了其独有的故障转移策略,完全无需哨兵的介入。尽管两者在故障转移上有诸多相似之处,但关键的不同在于:**Redis** 集群不依赖于外部的监视器(哨兵),而是依赖集群内部的节点来共同参与和决策来实现故障转移。

**接下来,让我们来看下 Redis 集群故障转移与恢复的具体步骤:**

**故障转移第一步:故障检测**

**主观下线**:

每个节点通过发送 **PING** 指令来周期性地对其他节点进行健康检查。若某节点在一个预定的 **cluster-node-timeout** 间隔内未接收到特定节点的 **PONG** 回复,它将标记该节点为主观下线状态。此状态仅代表单一节点的观察结果,并非集群共识。

**客观下线**

单一节点的主观观察并不触发集群级别的行动。只有当超过集群半数的节点都标记某节点为主观下线时,该节点才被认定为**客观下线**。这确保了故障转移的决策基于**多数节点**的共识,从而增加决策的准确性。

**首先,让我们来看下 Redis 集群中节点客观下线的判断过程。实际过程可能会更复杂,我这里只是简单举例说明**。

假设 Redis 集群目前存在 A、B、C、D、E 五个节点

步骤1:
A ------> B (节点 A 向 节点 B 发送 PING 请求,X 代表未收到回复消息)
X

步骤2:
A -------> 其他节点 (广播:“我认为B有问题!”)

广播消息格式:<当前节点ID> FAIL <被认为故障的节点ID> <当前节点的当前时间戳>

步骤3:
其他节点分别发送 PING 请求 检查与 B 的通信情况:

C -------> B
X
D -------> B
X
E -------> B

步骤4:
收集所有关于B的故障报告:

A: B故障
C: B故障
D: B故障
E: B响应正常

步骤5:
如果超过半数的节点报告 B 故障,B 被标记为客观下线。接下来就是故障转移第二步。



 **故障转移第二步:从节点晋升**

当一个主节点被确定为客观下线,需要从其关联的从节点中选举一个进行晋升,来替代失效的主节点。

主节点一般都会有多个从节点,这就带来了一个问题:**在多个从节点中,哪一个是最合适的候选者来晋升为新的主节点?**

为了确定最佳的从节点,Redis 集群使用以下的选举策略来挑选从节点。

- **数据同步进度**:

  每个从节点都会定期与其主节点进行数据同步。但因为网络或其他原因,有些从节点的数据可能更新。在故障转移时,拥有最新数据的从节点将被优先考虑,以减少数据丢失。

- **配置优先级**:

  在数据同步进度相同的情况下,优先级较高(值较小)的从节点会被选为新的主节点。

- **网络连接质量**:

  如果上面的两个条件都一样,Redis 则会考虑网络连接稳定、响应时间短的从节点成为新的主节点,因为它更有可能快速且准确地响应客户端的请求。

当从节点被选择并晋升为新的主节点后,整个集群需要进行**自动重新配置**来适应这种变化:

**自动重新配置**:

- **哈希槽分配**:新晋升的主节点会自动接管失效主节点的哈希槽,确保集群数据的连续访问性。
- **集群元数据更新**:为了适应拓扑变化,集群中的其他节点会更新其内部的元数据和配置(比如:新主节点的 ip 地址和 port 信息),以识别并与新的主节点通信。


 **故障转移第三步:故障恢复**

**1. 数据同步**:

为了确保数据的一致性和完整性,新晋升的主节点需要分别和其他从节点之间建立连接并进行数据同步:

- 那些继续作为从节点的节点会开始与新的主节点同步数据,这是为了确保从节点的数据是最新的。

- 同步过程可以是全量复制,也可以是部分复制,具体取决于自上次成功同步以来的数据变化量。

**2. 客户端重新连接**: 

故障期间,客户端可能遭受了中断。为了恢复正常的数据库操作,它们需要重新定位并连接到新晋升的主节点。

**3. 旧主节点的恢复**:

如果原先的失效主节点后来恢复了,并重新加入了集群,它将不再作为主节点,而是会被降级为从节点。

并且还需要与新的主节点同步数据,确保它自己的数据与新主节点的其他从节点数据一致。


**这里顺便提个问题:在上述故障恢复的第二步:客户端重新连接中,客户端如何知道新主节点的 IP 地址和端口信息的?**

我直接说答案吧,一般有以下两种做法:

**1. 客户端的 Redis Cluster 支持**:这是最常见的方法。许多现代的 Redis 客户端,如 Redis-py (Python)、Jedis (Java)、Redigo (Go) 等,都内置了对 Redis Cluster 的支持。这些客户端会自动处理 "MOVED" 错误,重新路由请求到正确的节点,并更新其内部的节点映射缓存。使用这种方法可以大大简化应用程序的开发和维护,因为客户端库会自动处理集群的变动。

**2. 集群发现**:对于那些需要更细粒度控制的应用或者使用没有内置 Redis Cluster 支持的客户端的场景,集群发现是一个有用的方法。通过定期发送 CLUSTER NODES 或 CLUSTER SLOTS 命令,应用程序可以自己管理和更新哈希槽到节点的映射。

返回集群中所有节点的信息。返回的信息包括每个节点的 ID、地址、角色(主/从)、与其他节点的关系、哈希槽范围等

CLUSTER NODES:

返回集群中哈希槽和其相应节点的映射信息。这对于理解哪个节点负责哪些哈希槽非常有用。

CLUSTER SLOTS


#### 重新分片

在我们了解 **Redis** 集群的节点角色和故障转移后,有一个非常重要的概念需要讲解:**重新分片**。随着数据量的增长或节点的变化,我们可能需要调整数据在集群中的分布。这就涉及到重新分片的过程。

**什么是重新分片?**

重新分片是一个过程,允许你在集群的不同节点之间移动哈希槽,这样可以确保数据在集群中均匀分布。

**为什么要进行重新分片?**

**集群扩展**:随着业务的增长,我们可能需要添加更多的节点到集群中。此时,需要将一部分数据迁移到新的节点。

**数据均衡**:为了防止某个节点数据过多导致性能瓶颈,我们可能需要将数据在各个节点之间重新分布。


**接下来,让我们来看下 Redis 集群是如何进行重新分片的?**

Redis 集群进行重新分片主要是通过以下 5 个步骤来进行的。

**步骤 1: 检查集群信息**

首先,通过 `redis-cli -c -p 7000 cluster nodes `命令,你获得了一个集群状态的信息,包括每个节点的ID、状态、IP 地址和端口,以及它们负责的哈希槽范围。这为我们提供了一个集群的总体布局和节点间的哈希槽分配情况。

**步骤 2: 识别负载不平衡的节点以及确定源节点和目标节点**

**观察节点负载**:这一步需要额外信息,比如使用 INFO 命令或其他监控工具来查看节点的内存使用、CPU 负载、网络带宽等指标。这些数据有助于我们识别出源节点和目标节点 。

**源节点选择**:源节点通常是那些负载较高的节点,你可能会选择那些内存使用率高、处理大量请求的节点作为哈希槽迁移的起点。

**目标节点选择**:目标节点则是负载相对较低,有能力接收更多哈希槽的节点。如果你最近向集群中添加了新节点,这些新节点也是很好的目标节点候选。

**步骤 3: 确定迁移的哈希槽**

现在,我们已经确定了要迁移哈希槽的源节点以及目标节点。接下来就要确定要迁移的源节点的哈希槽。

并没有直接的 Redis 命令可以告诉你每个哈希槽的详细数据量,因为 Redis 集群的设计是将数据分布在不同的哈希槽中,但你可以通过键的分布来间接获取每个哈希槽的数据量。这可能需要你编写脚本或使用现有的管理工具来帮助分析。

**比如:编写脚本统计每个哈希槽的键数量** 

可以遍历节点上的所有键,使用 `CLUSTER KEYSLOT` 命令确定每个键属于哪个哈希槽,然后对每个哈希槽的键数量进行计数。这需要通过 Redis 的命令行接口或客户端库来实现。

**这个脚本的基本逻辑是**:

- 连接到源节点。
- 使用 SCAN 命令遍历所有键(以避免大量键时的性能问题)。
- 对于 SCAN 返回的每个键,使用 CLUSTER KEYSLOT 命令确定其哈希槽。
- 维护一个计数器,对每个哈希槽内的键数量进行统计。

通过脚本收集到每个哈希槽内键的数量后,你可以分析这些数据来确定哪些哈希槽的数据量较大。通常,数据量大的哈希槽可能会导致源节点负载较高。基于这个分析,你可以选择数据量大(可能负载也较高)的哈希槽进行迁移。至此,我们便可以知道源节点要迁移的哈希槽。

**步骤 4: 执行重新分片**

经过上面的步骤,我们已经可以确定源节点、源节点要迁移的哈希槽以及将哈希槽要迁移的目标节点。

**接下来,就要执行重新分片:**

这个过程可以手动完成,也可以使用 Redis 的自动重新分片工具。手动重新分片提供了更好的控制,但自动重新分片更方便。

**手动重新分片**:

使用 **redis-cli --cluster reshard** 命令开始重新分片过程。该命令可以在集群的任意节点上执行。

`
redis-cli --cluster reshard <任意节点的IP>:<任意节点的端口>
`
该命令会启动一个交互式会话,询问你需要从哪个节点移动哈希槽,迁移哈希槽的范围,移动多少个哈希槽,以及目标节点。

**自动重新分片**:
如果你希望自动重新分片,可以使用 **--cluster-rebalance** 选项。这会尝试自动平衡集群的哈希槽分配。

redis-cli --cluster rebalance : --cluster-use-empty-masters
这里,: 是集群中任一节点的 IP 地址和端口号。
--cluster-use-empty-masters 选项允许命令使用没有分配任何哈希槽的节点。


**--cluster-use-empty-masters** :这个选项指示rebalance命令,在重平衡操作中包括那些当前未持有任何哈希槽的主节点。默认情况下,重平衡操作可能不会向这些空闲的主节点分配哈希槽,因为目标是在已有槽的节点之间进行平衡。启用这个选项后,重平衡逻辑会将这些空闲的主节点考虑在内,尝试向它们分配一些哈希槽,从而使得这些节点参与到集群的工作负载中。


**步骤五:分片完成后的检查**

**验证集群状态**

首先使用 `redis-cli --cluster check` 命令:

这个命令提供了一种快速检查 Redis 集群状态的方法。它会检测诸如配置不一致、连接问题及槽分配不均等的问题。

执行命令:`redis-cli --cluster check <any-node-ip>:<any-node-port>`。这里,<any-node-ip>:<any-node-port> 代表集群中任意节点的IP地址和端口号。

命令输出将报告集群健康状况,包括所有节点是否在线、槽是否被正确分配和覆盖全范围、以及是否有任何节点之间的配置不匹配。

**确保节点可达性和同步状态**:

检查命令输出中的每个节点状态,确认它们都是可达的并且处于联机状态。
确认所有的节点都正确同步了其他节点的状态信息,尤其是槽的分配信息。这一点对于集群的稳定运行和高可用性至关重要。

**监控重平衡效果**

**观察性能指标**:

- **延迟**:检查命令响应时间是否符合预期。任何异常的延迟增加都可能指示背后有问题,如网络问题或节点过载。
- **吞吐量**:监测集群处理请求的能力是否有所提升或至少保持在重平衡前的水平。
- **负载分布**:观察重平衡后节点的负载是否更均衡。使用INFO命令或Redis监控工具来查看各节点的负载情况。

**使用 INFO 命令和监控工具**:

- 执行 `redis-cli -h <node-ip> -p <node-port> INFO` 命令,可以获取到Redis节点的详细运行时信息和统计数据。这对于评估节点健康状况和性能非常有帮助。
- 考虑使用Redis 监控工具,如 Prometheus 配合Redis Exporter,或其他第三方监控解决方案。这些工具可以提供实时的性能图表,帮助你更直观地理解集群状态和性能变化。

**重新分片与客户端的挑战**

当我们讨论 **Redis** 集群的重新分片时,除了涉及哈希槽的迁移和节点之间的数据传输,还需要考虑一个重要的客户端层面的问题:**如何确保在迁移过程中数据的连续访问性**?

想象一下,当一个客户端正在访问一个特定的键值时,而这个键值恰好位于正在被迁移的哈希槽中,客户端会遇到什么情况呢?

**答案是**:客户端会收到一个 **ASK 重定向错误**。

**深入理解 ASK 重定向**

当客户端试图在源节点访问正处于迁移过程的哈希槽中的数据时,源节点并不直接拒绝,而是返回一个 **ASK** 重定向错误。这一错误实际上是 **Redis** 集群为保证数据访问连续性而设计的机制,它引导客户端去目标节点重新请求数据,从而避免服务中断。

**如何处理 ASK 重定向?**

**流程图**:

![ASK重定向.drawio (2).png](https://ucc.alicdn.com/pic/developer-ecology/o5dx7dlccd5jm_fdc7cc335ae84e31932b8341a520d02d.png)



**重要步骤说明**:

1.当客户端收到 **ASK** 重定向错误后,首先从错误信息中提取目标节点的 **IP** 地址和端口。

2.在向目标节点发送命令请求之前,客户端需要先发送一个特殊命令 **ASKING**。

3.发送 **ASKING** 命令后,客户端可以正常地重新向目标节点发送原命令请求。

4.客户端收到数据后,继续其后续的操作。


**这里提个问题** :上述的第二步中,在向目标节点发送命令请求之前,客户端需要先发送一个特殊命令 **ASKING**,该命令有何作用?

答:**ASKING** 命令是 **Redis** 集群在数据迁移时的特殊协议。当客户端收到一个 **ASK** 重定向提示时,它必须首先向目标节点发送 **ASKING** 命令。这是为了告诉目标节点:尽管该数据在常态下并不由你处理,但由于数据正在迁移,我需要暂时地从你这获取它。

值得注意的是,**ASKING** 命令的影响仅适用于紧随其后的那个请求。如果客户端后续还有其他请求收到ASK重定向,那么它必须为每一个这样的请求都重新发送 ASKING 命令。

#### 客户端与集群的交互

在我们讨论到 **Redis** 集群时,我们常常聚焦于后端的节点如何互相协作,而忽略了一个同样重要的角色:客户端。其实,客户端与集群的交互在 **Redis** 集群的日常运作中起到了举足轻重的作用。

**先来看下它们是怎样交互的?**

**交互流程图:**

![666666666666666666.png](https://ucc.alicdn.com/pic/developer-ecology/o5dx7dlccd5jm_2e5d34d99f984365bd133f0de941dbd4.png)


**重要步骤说明:**

**1. 判断节点是否可用**

  客户端首先向已知节点发送命令,如果某个节点暂时不可用或发生了故障转移,客户端可能会收到 CLUSTERDOWN 或 TRYAGAIN 错误。在这种情况下,客户端应该稍等片刻,并重新尝试操作,因为集群很可能在短时间内自我恢复。

**而如果当前节点时可用的,就执行第二步。**

**2. 计算槽号**

**计算方式**:  $slot = CRC16(key) \mod 16384$

**3.判断槽号是否为当前节点所负责**

如果该槽号是当前节点负责的,则直接在当前节点执行命令并返回结果给客户端。**如果不是当前节点所负责的,就执行下一步。**

**4. 客户端重定向**

如果槽号不是由当前节点负责的,节点会返回一个 **MOVED** 错误,告诉客户端正确的节点地址和端口。客户端则需要重新连接新节点,并再次发送命令,等待返回结果。

> **注意**:在 Redis 集群中,客户端可能遇到 MOVED 或 ASK 错误,表示要查询的数据在另一个节点上。客户端必须能自动重定向请求到正确节点。因此,使用集群时,需要选择支持这种重定向功能的客户端库。

### 集群的优点和缺点

那相比主从复制和哨兵机制,Redis 集群都有哪些优缺点呢?

#### 优点

**数据容量**:单个 Redis 节点可能受限于物理内存,而集群通过分片技术,将数据分散在多个节点上,允许我们存储超出单机物理内存限制的大量数据。

**性能提升**:由于数据和请求都被分散到多个节点上,集群可以更有效地处理高并发的读写请求,为应用提供更高的吞吐量。

**高可用性**:当单一节点出现问题时,集群中的其他节点可以接管其工作,确保服务不中断。这种自动的故障转移能力提高了系统的可靠性和稳定性。而主从复制的高可用需要哨兵的参与。

**扩展性**:随着业务的增长,可以简单地向集群添加更多节点,以满足存储和性能需求,而不需要停机或进行复杂的迁移。

#### 缺点

**配置复杂性**:设置 Redis 集群意味着要协调多个节点,包括主节点和从节点。与简单的主从复制相比,集群的槽分配和管理更为复杂。而且,当需要增加或减少节点时,调整集群配置可能需要手动干预。

**数据迁移**:在进行节点的增加或减少时,需要进行数据的重新分片,这可能会对性能产生短暂的影响。

**网络开销**:Redis 集群中多个节点需要频繁通信以同步数据。在大的集群里,这可能导致网络变慢和更高的带宽消耗。

**命令限制**:由于数据分片的原因,一些涉及多键操作的命令在集群模式下可能受到限制,比如 **HMSET**、**HMGET等**。



### Redis 集群的配置详解

yes 代表启用集群模式

cluster-enabled yes

Redis 用于保存集群状态信息的文件,状态信息包括:所有节点的信息(IP和Port)、它们的状态(故障,在线)、哈希槽信息等

cluster-config-file nodes-6379.conf

如果一个节点在15000毫秒(15秒)内没有收到来自另一个节点的PONG响应(即对PING请求的回应),那么它会认为那个节点是不可达或失效的(处于主观下线状态)。

cluster-node-timeout 15000

这个配置项决定了一个从节点断开与主节点连接多久后,会被认为不能进行故障转移和晋升为主节点。默认是10s

cluster-replica-validity-factor 10

当一个主节点失效,为了晋升其下的从节点成为新的主节点,至少需要有多少个从节点在线,否则不会进行晋升过程。

cluster-migration-barrier 1

是否允许从节点在集群的不同主节点之间迁移。这有助于在负载不均的情况下平衡从节点。

cluster-allow-replica-migration yes

如果设置为 "yes",则集群要保证所有的哈希槽都必须有一个主节点来管理它,否则整个集群将停止服务。

cluster-require-full-coverage yes

这个配置选项决定了当主节点失效时,从节点是否可以自动升级为主节点。置为 "no" 允许从节点尝试变为新的主节点。设置为 "yes" 则不允许这种晋升。

cluster-replica-no-failover no

该配置项决定了在集群出现问题时,是否允许在从节点上进行读操作。设置为 "no" 时,从节点不允许读取。如果设置为 "yes",即使集群有问题,从节点还是可以读取。

cluster-allow-reads-when-down no
```

总结

在本文中,我们深入探讨了 Redis 的核心架构组件和它们如何相互作用以确保数据的可靠性和高性能。通过主从复制,Redis 实现了数据的冗余备份,为读操作提供了负载分担。而哨兵系统则为我们提供了自动故障转移,确保了系统的高可用性。最后,通过 Redis 集群,我们可以实现数据的水平分片,提供了更大规模的数据存储和处理能力。

为了让大家对 Redis 架构有更加深入的了解,我又对其进行了总结:

主从复制

  • 目的:通过复制数据到多个副本,主从复制增强了数据的可用性和读取性能。
  • 工作原理:一个主节点负责写操作,同时将数据变更同步到一个或多个从节点,从节点主要处理读请求。
  • 局限性:主节点故障时,需人工干预进行故障恢复,可能导致数据丢失或服务不可用。
  • 应用场景:适用于读写分离,提高读取性能的场景。

哨兵

  • 目的:自动化处理故障转移,提高系统的可用性和稳定性。
  • 工作原理:通过监控主从节点状态,自动进行主节点故障的检测、选举和故障转移。
  • 优点:自动化故障恢复,减少了人工干预的需要,提高了系统的鲁棒性。
  • 应用场景:适用于需要高可用性解决方案的场景。

集群

  • 目的:通过数据分片和自动故障转移,实现数据的水平扩展和高可用性。
  • 工作原理:将数据分布在多个节点上,每个节点存储一部分数据,通过哈希槽来定位数据的位置。
  • 优缺点:提供了高性能的数据读写能力和良好的伸缩性,但配置和管理相对复杂。
  • 应用场景:适用于大规模数据存储和高并发访问的场景。

理论掌握了,接下来就是实战。下篇文章,我将带领大家实际搭建 Redis 的主从复制、哨兵与集群。想知道如何实际操作?那可以先关注我的公众号「跟着小康学编程」,这里不仅有持续更新的计算机基础相关的文章,还有如何系统学习 C、C++,Linux 系统编程以及网络编程的文章等。

大家可以关注我 ,具体可访问 :关注小康微信公众号

另外大家在阅读这篇文章的时候,如果觉得有问题的或者有不理解的知识点,欢迎大家评论去询问。我看到就会回复大家的。大家也可以加我的微信:jkfwdkf,, 备注「加群」 ,有任何不理解得都可以咨询我。

最后,大家如果觉得我写的还不错,也希望大家能够帮忙点个赞,感谢大家的关注!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
分布式计算 Kubernetes Hadoop
大数据-82 Spark 集群模式启动、集群架构、集群管理器 Spark的HelloWorld + Hadoop + HDFS
大数据-82 Spark 集群模式启动、集群架构、集群管理器 Spark的HelloWorld + Hadoop + HDFS
148 6
|
8天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
11天前
|
人工智能 云计算 网络架构
阿里云引领智算集群网络架构的新一轮变革
11月8日~10日在江苏张家港召开的CCF ChinaNet(即中国网络大会)上,众多院士、教授和业界技术领袖齐聚一堂,畅谈网络未来的发展方向,聚焦智算集群网络的创新变革。
阿里云引领智算集群网络架构的新一轮变革
|
12天前
|
负载均衡 Dubbo 算法
集群容错架构设计
集群容错架构设计
23 1
集群容错架构设计
|
10天前
|
人工智能 运维 网络架构
阿里云引领智算集群网络架构的新一轮变革
11月8日至10日,CCF ChinaNet(中国网络大会)在江苏张家港召开,众多院士、教授和技术领袖共聚一堂,探讨网络未来发展方向。阿里云研发副总裁蔡德忠发表主题演讲,展望智算技术发展趋势,提出智算网络架构变革的新思路,发布高通量以太网协议和ENode+超节点系统规划,引起广泛关注。阿里云HPN7.0引领智算以太网生态蓬勃发展,成为业界标杆。未来,X10规模的智算集群将面临新的挑战,Ethernet将成为主流方案,推动Scale up与Scale out的融合架构,提升整体系统性能。
|
8天前
|
存储 缓存 NoSQL
【赵渝强老师】Memcached集群的架构
Memcached 是一个高性能的分布式内存对象缓存系统,通过在内存中维护一个巨大的 Hash 表来存储各种格式的数据,如图像、视频、文件及数据库检索结果等。它主要用于减轻数据库压力,提高网站系统的性能。Memcached 不支持数据持久化,因此仅作为缓存技术使用。其数据分布式存储由客户端应用程序实现,而非服务端。
【赵渝强老师】Memcached集群的架构
|
8天前
|
调度 Docker 容器
【赵渝强老师】Docker Swarm集群的体系架构
Docker Swarm自1.12.0版本起集成至Docker引擎,无需单独安装。它内置服务发现功能,支持跨多服务器或宿主机创建容器,形成集群提供服务。相比之下,Docker Compose仅限于单个宿主机。Docker Swarm采用主从架构,Swarm Manager负责管理和调度集群中的容器资源,用户通过其接口发送指令,Swarm Node根据指令创建容器运行应用。
|
1月前
|
负载均衡 安全 调度
Docker Swarm集群架构
【10月更文挑战第8天】
57 1
|
1月前
|
存储 NoSQL 大数据
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
33 3
|
1月前
|
消息中间件 分布式计算 Kafka
大数据-98 Spark 集群 Spark Streaming 基础概述 架构概念 执行流程 优缺点
大数据-98 Spark 集群 Spark Streaming 基础概述 架构概念 执行流程 优缺点
39 0