Redis 고가용성 클러스터를 단계별로 이해해 보세요.

WBOY
풀어 주다: 2022-03-04 17:03:45
앞으로
2608명이 탐색했습니다.

이 기사에서는 클러스터 관련 문제를 주로 소개하는 Redis 관련 지식을 제공합니다. Redis 클러스터는 분산 데이터베이스 솔루션으로, 샤딩을 통해 데이터를 공유하고 복제 및 전송 기능을 제공하는 데 도움이 되기를 바랍니다. 모든 사람.

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

추천 학습: Redis 학습 튜토리얼

여러 Redis 고가용성 솔루션. 포함: "마스터-슬레이브 모드", "센티넬 메커니즘" 및 "센티넬 클러스터".

  • "마스터-슬레이브 모드"는 읽기와 쓰기의 분리, 읽기 압력의 공유, 데이터 백업, 다중 복사본 제공의 장점을 가지고 있습니다.
  • "Sentinel Mechanism"은 마스터 노드에 장애가 발생한 후 슬레이브 노드를 마스터 노드로 자동 승격할 수 있으며, 수동 개입 없이 서비스를 복원할 수 있습니다.
  • "Sentinel Cluster"는 단일 시스템 Sentinel로 인해 발생하는 단일 장애 지점 및 "오판" 문제를 해결합니다.

Redis는 가장 단순한 독립형 버전에서 데이터 지속성, 마스터-슬레이브 다중 복사본, 센티널 클러스터로 발전했습니다. 이러한 최적화를 통해 성능과 안정성이 더욱 높아졌습니다.

그러나 시간이 지남에 따라 회사의 사업 규모는 폭발적으로 성장했습니다. 현재 아키텍처 모델이 여전히 그렇게 많은 트래픽을 감당할 수 있을까요?

예를 들어 다음과 같은 요구 사항이 있습니다. Redis를 사용하여 5천만개의 키-값 쌍을 저장하고 각 키-값 쌍은 약 512B이므로 빠르게 배포할 수 있습니다. 외부 서비스를 제공하고 클라우드 호스트를 사용하여 Redis 인스턴스를 실행하는 경우 클라우드 호스트의 메모리 용량을 선택하는 방법은 무엇입니까? 5000 万个键值对,每个键值对大约是 512B,为了能快速部署并对外提供服务,我们采用云主机来运行 Redis 实例,那么,该如何选择云主机的内存容量呢?

通过计算,这些键值对所占的内存空间大约是 25GB(5000 万 *512B)。

想到的第一个方案就是:选择一台 32GB 内存的云主机来部署 Redis。因为 32GB 的内存能保存所有数据,而且还留有 7GB,可以保证系统的正常运行。

同时,还采用 RDB 对数据做持久化,以确保 Redis 实例故障后,还能从 RDB 恢复数据。

但是,在使用的过程中会发现,Redis 的响应有时会非常慢。通过 INFO命令 查看 Redis 的latest_fork_usec指标值(表示最近一次 fork 的耗时),结果发现这个指标值特别高。

这跟 Redis 的持久化机制有关系。

在使用 RDB 进行持久化时,Redis 会 fork 子进程来完成,fork 操作的用时和 Redis 的数据量是正相关的,而 fork 在执行时会阻塞主线程。数据量越大,fork 操作造成的主线程阻塞的时间越长。

所以,在使用 RDB 对 25GB 的数据进行持久化时,数据量较大,后台运行的子进程在 fork 创建时阻塞了主线程,于是就导致 Redis 响应变慢了。

显然这个方案是不可行的,我们必须要寻找其他的方案。

如何保存更多数据?

为了保存大量数据,我们一般有两种方法:「纵向扩展」和「横向扩展」:

  • 纵向扩展:升级单个 Redis 实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的 CPU;
  • 横向扩展:横向增加当前 Redis 实例的个数。

首先,「纵向扩展」的好处是,实施起来简单、直接。不过,这个方案也面临两个潜在的问题。

  • 第一个问题是,当使用 RDB 对数据进行持久化时,如果数据量增加,需要的内存也会增加,主线程 fork 子进程时就可能会阻塞。
  • 第二个问题:纵向扩展会受到硬件和成本的限制。 这很容易理解,毕竟,把内存从 32GB 扩展到 64GB 还算容易,但是,要想扩充到 1TB,就会面临硬件容量和成本上的限制了。

与「纵向扩展」相比,「横向扩展」是一个扩展性更好的方案。这是因为,要想保存更多的数据,采用这种方案的话,只用增加 Redis 的实例个数就行了,不用担心单个实例的硬件和成本限制。

Redis 集群就是基于「横向扩展」实现的 ,通过启动多个 Redis 实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。

Redis 集群

Redis 集群是一种分布式数据库方案,集群通过分片sharding,也可以叫切片

계산에 따르면 이러한 키-값 쌍이 차지하는 메모리 공간은 약 25GB(5천만 *512B)입니다.

가장 먼저 생각나는 솔루션은 Redis를 배포할 32GB 메모리를 갖춘 클라우드 호스트를 선택하는 것입니다. 32GB의 메모리가 모든 데이터를 저장할 수 있기 때문에 시스템의 정상적인 작동을 보장하기 위해 아직 7GB가 남아 있습니다. 🎜🎜동시에 RDB는 Redis 인스턴스 실패 후 RDB에서 데이터를 복구할 수 있도록 데이터를 유지하는 데에도 사용됩니다. 🎜🎜그러나 사용 중에 Redis의 응답이 때때로 매우 느린 것을 발견할 수 있습니다. INFO 명령어를 이용하여 Redis의 latest_fork_usec 지표 값(최근 포크에 소요된 시간)을 확인한 결과 이 ​​지표 값이 유난히 높은 것으로 나타났다. 🎜🎜이것은 Redis의 지속성 메커니즘과 관련이 있습니다. 🎜🎜지속성을 위해 RDB를 사용하는 경우 Redis는 작업을 완료하기 위해 fork합니다. fork 작업 시간은 양과 정비례합니다. Redis의 데이터 fork는 실행 중에 기본 스레드를 차단합니다. 데이터 양이 많을수록 포크 작업으로 인해 메인 스레드가 차단되는 시간이 길어집니다. 🎜🎜그래서 RDB를 사용하여 25GB의 데이터를 유지하는 경우 데이터의 양이 많고 fork 시 백그라운드에서 실행 중인 하위 프로세스가 차단됩니다. 기본 스레드로 인해 Redis 응답이 느려집니다. 🎜🎜분명히 이 솔루션은 실현 가능하지 않으며 다른 솔루션을 찾아야 합니다. 🎜

데이터를 더 많이 저장하는 방법은 무엇인가요?

🎜대량의 데이터를 절약하기 위해 일반적으로 "수직 확장"과 "수평 확장"의 두 가지 방법이 있습니다. 🎜🎜🎜수직 확장 : 메모리 용량 증가, 디스크 용량 증가, 더 높은 구성의 CPU 사용을 포함하여 인스턴스의 단일 Redis 리소스 구성을 업그레이드합니다. 🎜🎜수평 확장: 현재 Redis 인스턴스 수를 수평으로 늘립니다. 🎜🎜🎜우선 "수직 확장"의 장점은 구현이 간단하고 간단하다는 것입니다. 그러나 이 솔루션은 두 가지 잠재적인 문제에도 직면해 있습니다. 🎜🎜🎜첫 번째 문제는 데이터를 유지하기 위해 RDB를 사용할 때 데이터 양이 증가하면 필요한 메모리도 증가하고 하위 프로세스를 fork할 때 메인 스레드가 차단될 수 있다는 것입니다. 🎜🎜두 번째 질문:수직 확장은 하드웨어와 비용으로 인해 제한됩니다. 결국 메모리를 32GB에서 64GB로 확장하는 것은 쉽지만, 1TB까지 확장하려면 하드웨어 용량과 비용의 한계에 직면하게 됩니다. 🎜🎜🎜"수직 확장"에 비해 "수평 확장"이 더 나은 확장성 솔루션입니다. 더 많은 데이터를 절약하고 이 솔루션을 사용하려면 Redis 인스턴스 수만 늘리면 단일 인스턴스의 하드웨어 및 비용 제한에 대해 걱정할 필요가 없기 때문입니다. 🎜🎜Redis 클러스터는 "수평 확장"을 기반으로 구현됩니다. 여러 Redis 인스턴스를 시작하여 클러스터를 구성한 후 특정 규칙에 따라 수신된 데이터를 여러 부분으로 나누고 각 부분을 하나의 인스턴스를 사용하여 저장합니다. 🎜

Redis 클러스터

🎜Redis 클러스터는 분산 데이터베이스 솔루션입니다. 클러스터는 샤딩(샤딩, 슬라이싱이라고도 함)을 통과합니다. ) 데이터를 공유하고 복제 및 장애 조치 기능을 제공합니다. 🎜🎜방금 했던 시나리오로 돌아가서, 25GB 데이터를 5개로 균등하게 나누고(물론 균등하게 나눌 필요는 없음), 5개의 인스턴스를 사용하여 저장한다면 각 인스턴스에는 5GB만 저장하면 됩니다. 데이터. 아래 그림과 같이: 🎜

Redis 고가용성 클러스터를 단계별로 이해해 보세요.
그러면 슬라이싱 클러스터에서는 인스턴스가 5GB 데이터에 대한 RDB를 생성할 때 데이터의 양이 훨씬 적고 fork 하위 프로세스는 일반적으로 오랫동안 메인 스레드를 차단하지 않습니다. . fork 子进程一般不会给主线程带来较长时间的阻塞。

采用多个实例保存数据切片后,我们既能保存 25GB 数据,又避免了 fork 子进程阻塞主线程而导致的响应突然变慢。

在实际应用 Redis 时,随着业务规模的扩展,保存大量数据的情况通常是无法避免的。而 Redis 集群,就是一个非常好的解决方案。

下面我们开始研究如何搭建一个 Redis 集群?

搭建 Redis 集群

一个 Redis 集群通常由多个节点组成,在刚开始的时候,每个节点都是相互独立地,节点之间没有任何关联。要组建一个可以工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多节点的集群

我们可以通过 CLUSTER MEET 命令,将各个节点连接起来:

CLUSTER MEET <ip> <port></port></ip>
로그인 후 복사
  • ip:待加入集群的节点 ip
  • port:待加入集群的节点 port

命令说明:通过向一个节点 A 发送 CLUSTER MEET 命令,可以让接收命令的节点 A 将另一个节点 B 添加到节点 A 所在的集群中。

这么说有点抽象,下面看一个例子。

假设现在有三个独立的节点 127.0.0.1:7001127.0.0.1:7002127.0.0.1:7003

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

我们首先使用客户端连上节点 7001

$ redis-cli -c -p 7001
로그인 후 복사

然后向节点 7001 发送命令,将节点 7002 添加到 7001 所在的集群里:

127.0.0.1:7001> CLUSTER MEET 127.0.0.1 7002
로그인 후 복사

同样的,我们向 7003 发送命令,也添加到 70017002 所在的集群。

127.0.0.1:7001> CLUSTER MEET 127.0.0.1 7003
로그인 후 복사

通过 CLUSTER NODES 命令可以查看集群中的节点信息。

Redis 고가용성 클러스터를 단계별로 이해해 보세요.
现在集群中已经包含 700170027003 三个节点。不过,在使用单个实例的时候,数据存在哪儿,客户端访问哪儿,都是非常明确的。但是,切片集群不可避免地涉及到多个实例的分布式管理问题

要想把切片集群用起来,我们就需要解决两大问题:

  • 数据切片后,在多个实例之间如何分布?
  • 客户端怎么确定想要访问的数据在哪个实例上?

接下来,我们就一个个地解决。

数据切片和实例的对应分布关系

在切片集群中,数据需要分布在不同实例上,那么,数据和实例之间如何对应呢?

这就和接下来要讲的 Redis Cluster 方案有关了。不过,我们要先弄明白切片集群和 Redis Cluster 的联系与区别。

在 Redis 3.0 之前,官方并没有针对切片集群提供具体的方案。从 3.0 开始,官方提供了一个名为 Redis Cluster 的方案,用于实现切片集群。

实际上,切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。 Redis Cluster 方案中就规定了数据和实例的对应规则。

具体来说, Redis Cluster 方案采用 哈希槽(Hash Slot),来处理数据和实例之间的映射关系。

哈希槽与 Redis 实例映射

Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽(2^14),这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中。

在上面我们分析的,通过 CLUSTER MEET 命令将 700170027003 三个节点连接到同一个集群里面,但是这个集群目前是处于下线状态的,因为集群中的三个节点没有分配任何槽

那么,这些哈希槽又是如何被映射到具体的 Redis 实例上的呢?

我们可以使用 CLUSTER MEET 命令手动建立实例间的连接,形成集群,再使用CLUSTER ADDSLOTS 命令,指定每个实例上的哈希槽个数。

CLUSTER ADDSLOTS <slot> [slot ...]</slot>
로그인 후 복사

Redis5.0 提供 CLUSTER CREATE

여러 인스턴스를 사용하여 데이터 조각을 저장한 후에는 25GB의 데이터를 저장할 수 있을 뿐만 아니라 메인 스레드를 차단하는 fork 하위 프로세스로 인해 발생하는 갑작스러운 응답 속도 저하도 방지할 수 있습니다. 🎜🎜실제로 Redis를 적용하게 되면 사업 규모가 커지면서 많은 양의 데이터를 저장하는 것은 불가피한 경우가 많습니다. 그리고 Redis 클러스터는 매우 좋은 솔루션입니다. 🎜🎜Redis 클러스터 구축 방법을 시작해볼까요? 🎜

Redis 클러스터 구축

🎜Redis 클러스터는 일반적으로 여러 노드로 구성됩니다. 처음에는 각 노드가 서로 독립적이며 노드 간에 관계가 없습니다. 작동하는 클러스터를 형성하려면 독립 노드를 연결하여 여러 노드를 포함하는 클러스터를 형성해야 합니다. 🎜🎜CLUSTER MEET 명령을 통해 각 노드를 연결할 수 있습니다: 🎜
127.0.0.1:7001> CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
로그인 후 복사
로그인 후 복사
  • ip: 클러스터에 추가할 노드의 IP
  • port: to 클러스터 노드 포트에 추가
🎜명령 설명: 노드 A에 CLUSTER MEET 명령을 전송하면 명령을 받은 노드 A가 다른 노드 B를 추가할 수 있습니다. 클러스터의 노드 A. 🎜🎜이것은 다소 추상적입니다. 예를 살펴보겠습니다. 🎜🎜이제 세 개의 독립 노드 127.0.0.1:7001, 127.0.0.1:7002, 127.0.0.1:7003이 있다고 가정해 보겠습니다. 🎜🎜여기에 이미지 설명 삽입🎜🎜먼저 클라이언트를 사용합니다 터미널을 노드 7001에 연결합니다: 🎜
127.0.0.1:7002> CLUSTER ADDSLOTS 5001 5002 5003 5004 ... 10000
로그인 후 복사
로그인 후 복사
🎜 그런 다음 노드 7001에 명령을 보내 노드 70027001에 추가합니다. > 위치한 클러스터에서: 🎜
127.0.0.1:7003> CLUSTER ADDSLOTS 10001 10002 10003 10004 ... 16383
로그인 후 복사
로그인 후 복사
🎜마찬가지로 7003에 명령을 보내고 70017002가 있는 클러스터에도 추가합니다. 코드>가 위치해 있습니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">crc16(key,keylen) &amp; 0x3FFF;</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div> <blockquote>🎜 <code>CLUSTER NODES 명령을 통해 클러스터의 노드 정보를 볼 수 있습니다. 🎜
🎜여기에 이미지 설명 삽입🎜 이제 클러스터에는 이미 7001, 70027003의 3개 노드가 포함되어 있습니다. 그러나 단일 인스턴스를 사용하면 데이터가 어디에 있는지, 클라이언트가 데이터에 액세스하는 위치가 매우 명확합니다. 그러나 클러스터를 슬라이싱하면 필연적으로 여러 인스턴스의 분산 관리 문제가 발생합니다. 🎜🎜슬라이싱 클러스터를 사용하려면 두 가지 주요 문제를 해결해야 합니다. 🎜
  • 데이터가 슬라이싱된 후 여러 인스턴스에 어떻게 분산됩니까?
  • 클라이언트는 액세스하려는 데이터가 어느 인스턴스에 있는지 어떻게 결정합니까?
🎜다음으로 하나씩 풀어보겠습니다. 🎜

데이터 슬라이스와 인스턴스 간의 대응 분포 관계

🎜슬라이싱 클러스터에서는 데이터가 서로 다른 인스턴스에 분산되어야 합니다. 그렇다면 데이터와 인스턴스 간의 대응은 어떻게 이루어 집니까? 🎜🎜다음에 말씀드릴 Redis Cluster 솔루션과 관련된 내용입니다. 하지만 먼저 슬라이싱 클러스터와 Redis Cluster의 연결과 차이점을 이해해야 합니다. 🎜
🎜Redis 3.0 이전에는 공식적으로 클러스터 슬라이싱에 대한 구체적인 솔루션을 제공하지 않았습니다. 3.0부터는 슬라이싱 클러스터를 구현하기 위해 Redis Cluster라는 공식 솔루션을 사용합니다. 🎜
🎜사실 클러스터 슬라이싱은 대량의 데이터를 저장하기 위한 일반적인 메커니즘입니다. 이 메커니즘은 다양한 구현 솔루션을 가질 수 있습니다. Redis Cluster 체계는 데이터와 인스턴스에 해당하는 규칙을 규정합니다. 🎜🎜구체적으로 Redis Cluster 솔루션은 해시 슬롯(해시 슬롯)을 사용하여 데이터와 인스턴스 간의 매핑 관계를 처리합니다. 🎜

해시 슬롯 및 Redis 인스턴스 매핑

🎜Redis 클러스터 솔루션에서 슬라이스 클러스터에는 총 16384개의 해시 슬롯(2^14)이 있습니다. , 이러한 해시 슬롯은 데이터 파티션과 유사하며 각 키-값 쌍은 해당 키에 따라 해시 슬롯에 매핑됩니다. 🎜🎜위 분석에서 7001, 7002, 7003 3개 노드는 CLUSTER MEET을 통해 연결되어 있습니다. 명령 동일한 클러스터로 이동하지만 이 클러스터는 현재 오프라인 상태입니다. 클러스터의 세 노드에 할당된 슬롯이 없기 때문입니다. 🎜🎜그렇다면 이러한 해시 슬롯은 특정 Redis 인스턴스에 어떻게 매핑됩니까? 🎜🎜CLUSTER MEET 명령을 사용하여 인스턴스 간 연결을 수동으로 설정하여 클러스터를 형성한 다음 CLUSTER ADDSLOTS 명령을 사용하여 각 인스턴스의 해시 슬롯 수를 지정할 수 있습니다. 사례. 🎜
MOVED  <slot> <ip>:<port></port></ip></slot>
로그인 후 복사
로그인 후 복사
🎜Redis5.0은 클러스터를 생성하는 CLUSTER CREATE 명령을 제공합니다. 이 명령을 사용하면 Redis는 이러한 슬롯을 클러스터 인스턴스에 균등하게 자동으로 배포합니다. 🎜

举个例子,我们通过以下命令,给 700170027003 三个节点分别指派槽。

将槽 0 ~ 槽5000 指派给 给 7001

127.0.0.1:7001> CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
로그인 후 복사
로그인 후 복사

将槽 5001 ~ 槽10000 指派给 给 7002

127.0.0.1:7002> CLUSTER ADDSLOTS 5001 5002 5003 5004 ... 10000
로그인 후 복사
로그인 후 복사

将槽 10001~ 槽 16383 指派给 给 7003

127.0.0.1:7003> CLUSTER ADDSLOTS 10001 10002 10003 10004 ... 16383
로그인 후 복사
로그인 후 복사

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

当三个 CLUSTER ADDSLOTS 命令都执行完毕之后,数据库中的 16384 个槽都已经被指派给了对应的节点,此时集群进入上线状态。

通过哈希槽,切片集群就实现了数据到哈希槽、哈希槽再到实例的分配。

但是,即使实例有了哈希槽的映射信息,客户端又是怎么知道要访问的数据在哪个实例上呢?

客户端如何定位数据?

一般来说,客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端。但是,在集群刚刚创建的时候,每个实例只知道自己被分配了哪些哈希槽,是不知道其他实例拥有的哈希槽信息的。

那么,客户端是如何可以在访问任何一个实例时,就能获得所有的哈希槽信息呢?

Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散。当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。

客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给相应的实例发送请求了。

当客户端向节点请求键值对时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:

  • 如果键所在的槽刚好指派给了当前节点,那么节点会直接执行这个命令;
  • 如果没有指派给当前节点,那么节点会向客户端返回一个 MOVED 错误,然后重定向(redirect)到正确的节点,并再次发送之前待执行的命令。

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

计算键属于哪个槽

节点通过以下算法来定义 key 属于哪个槽:

crc16(key,keylen) & 0x3FFF;
로그인 후 복사
로그인 후 복사
  • crc16:用于计算 key 的 CRC-16 校验和
  • 0x3FFF:换算成 10 进制是 16383
  • & 0x3FFF:用于计算出一个介于 0~16383 之间的整数作为 key 的槽号。

通过 CLUSTER KEYSLOT <key></key>命令可以查看 key 属于哪个槽。

判断槽是否由当前节点负责处理

当节点计算出 key 所属的 槽 i 之后,节点会判断 槽 i 是否被指派了自己。那么如何判断呢?

每个节点会维护一个 「slots数组」,节点通过检查 slots[i] ,判断 槽 i 是否由自己负责:

  • 如果说 slots[i] 对应的节点是当前节点的话,那么说明 槽 i 由当前节点负责,节点可以执行客户端发送的命令;
  • 如果说 slots[i] 对应的不是当前节点,节点会根据 slots[i] 所指向的节点向客户端返回 MOVED 错误,指引客户端转到正确的节点。

MOVED 错误

格式:

MOVED  <slot> <ip>:<port></port></ip></slot>
로그인 후 복사
로그인 후 복사
  • slot:键所在的槽
  • ip:负责处理槽 slot 节点的 ip
  • port:负责处理槽 slot 节点的 port

比如:MOVED 10086 127.0.0.1:7002,表示,客户端请求的键值对所在的哈希槽 10086,实际是在 127.0.0.1:7002 这个实例上。

通过返回的 MOVED 命令,就相当于把哈希槽所在的新实例的信息告诉给客户端了。

这样一来,客户端就可以直接和 7002 连接,并发送操作请求了。

同时,客户端还会更新本地缓存,将该槽与 Redis 实例对应关系更新正确。

集群模式的 redis-cli 客户端在接收到 MOVED 错误时,并不会打印出 MOVED 错误,而是根据 MOVED 错误自动进行节点转向,并打印出转向信息,所以我们是看不见节点返回的 MOVED 错误的。而使用单机模式的 redis-cli 客户端可以打印MOVED 错误。

其实,Redis 告知客户端重定向访问新实例分两种情况:MOVEDASK 。下面我们分析下 ASK 重定向命令的使用方法。

重新分片

在集群中,实例和哈希槽的对应关系并不是一成不变的,最常见的变化有两个:

  • 在集群中,实例有新增或删除,Redis 需要重新分配哈希槽;
  • 为了负载均衡,Redis 需要把哈希槽在所有实例上重新分布一遍。

重新分片可以在线进行,也就是说,重新分片的过程中,集群不需要下线。

举个例子,上面提到,我们组成了 700170027003 三个节点的集群,我们可以向这个集群添加一个新节点127.0.0.1:7004

$ redis-cli -c -p 7001
127.0.0.1:7001> CLUSTER MEET 127.0.0.1 7004
OK
로그인 후 복사

然后通过重新分片,将原本指派给节点 7003 的槽 15001 ~ 槽 16383 改为指派给 7004
Redis 고가용성 클러스터를 단계별로 이해해 보세요.
在重新分片的期间,源节点向目标节点迁移槽的过程中,可能会出现这样一种情况:如果某个槽的数据比较多,部分迁移到新实例,还有一部分没有迁移咋办?

在这种迁移部分完成的情况下,客户端就会收到一条 ASK 报错信息。

ASK 错误

如果客户端向目标节点发送一个与数据库键有关的命令,并且这个命令要处理的键正好属于被迁移的槽时:

  • 源节点会先在自己的数据库里查找指定的键,如果找到的话,直接执行命令;
  • 相反,如果源节点没有找到,那么这个键就有可能已经迁移到了目标节点,源节点就会向客户端发送一个 ASK 错误,指引客户端转向目标节点,并再次发送之前要执行的命令。

看起来好像有点复杂,我们举个例子来解释一下。

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

如上图所示,节点 7003 正在向 7004 迁移 槽 16383,这个槽包含 helloworld,其中键 hello 还留在节点 7003,而 world 已经迁移到 7004

我们向节点 7003 发送关于 hello 的命令 这个命令会直接执行:

127.0.0.1:7003> GET "hello"
"you get the key 'hello'"
로그인 후 복사

如果我们向节点 7003 发送 world 那么客户端就会被重定向到 7004

127.0.0.1:7003>  GET "world"
-> (error) ASK 16383 127.0.0.1:7004
로그인 후 복사

客户端在接收到 ASK 错误之后,先发送一个 ASKING 命令,然后在发送 GET "world" 命令。

ASKING 命令用于打开节点的 ASKING 标识,打开之后才可以执行命令。

ASK 和 MOVED 的区别

ASK 错误和 MOVED 错误都会导致客户端重定向,它们的区别在于:

  • MOVED 错误代表槽的负责权已经从一个节点转移到了另一个节点:在客户端收到关于 槽 iMOVED 错误之后,客户端每次遇到关于 槽 i 的命令请求时,都可以直接将命令请求发送至 MOVED 错误指向的节点,因为该节点就是目前负责 槽 i的节点。
  • 而 ASK 只是两个节点迁移槽的过程中的一种临时措施:在客户端收到关于 槽 iASK 错误之后,客户端只会在接下来的一次命令请求中将关于 槽 i 的命令请求发送到 ASK 错误指向的节点,但是 ,如果客户端再次请求 槽 i 中的数据,它还是会给原来负责 槽 i 的节点发送请求

这也就是说,ASK 命令的作用只是让客户端能给新实例发送一次请求,而且也不会更新客户端缓存的哈希槽分配信息。而不像 MOVED 命令那样,会更改本地缓存,让后续所有命令都发往新实例。

我们现在知道了 Redis 集群的实现原理。下面我们再来分析下,Redis 集群如何实现高可用的呢?

复制与故障转移

Redis 集群中的节点也是分为主节点和从节点。

  • 主节点用于处理槽
  • 从节点用于复制主节点,如果被复制的主节点下线,可以代替主节点继续提供服务。

举个例子,对于包含 7001 ~ 7004 的四个主节点的集群,可以添加两个节点:70057006。并将这两个节点设置为 7001 的从节点。

设置从节点命令:

CLUSTER REPLICATE <node_id></node_id>
로그인 후 복사

如图:

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

如果此时,主节点 7001 下线,那么集群中剩余正常工作的主节点将在 7001 的两个从节点中选出一个作为新的主节点。

例如,节点 7005 被选中,那么原来由节点 7001 负责处理的槽会交给节点 7005 处理。而节点 7006 会改为复制新主节点 7005。如果后续 7001 重新上线,那么它将成为 7005 的从节点。如下图所示:

Redis 고가용성 클러스터를 단계별로 이해해 보세요.

故障检测

集群中每个节点会定期向其他节点发送 PING 消息,来检测对方是否在线。如果接收消息的一方没有在规定时间内返回 PONG 消息,那么接收消息的一方就会被发送方标记为「疑似下线」。

集群中的各个节点会通过互相发消息的方式来交换各节点的状态信息。

节点的三种状态:

  • 在线状态
  • 疑似下线状态 PFAIL
  • 已下线状态 FAIL

一个节点认为某个节点失联了并不代表所有的节点都认为它失联了。在一个集群中,半数以上负责处理槽的主节点都认定了某个主节点下线了,集群才认为该节点需要进行主从切换。

Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。

我们都知道,哨兵机制可以通过监控、自动切换主库、通知客户端实现故障自动切换。那么 Redis Cluster 又是如何实现故障自动转移呢?

故障转移

当一个从节点发现自己正在复制的主节点进入了「已下线」状态时,从节点将开始对下线主节点进行故障切换。

故障转移的执行步骤:

  1. 在复制下线主节点的所有从节点里,选中一个从节点
  2. 被选中的从节点执行 SLAVEOF no one 命令,成为主节点
  3. 新的主节点会撤销所有对已下线主节点的槽指派,将这些槽全部指派给自己
  4. 新的主节点向集群广播一条 PONG 消息,让集群中其他节点知道,该节点已经由从节点变为主节点,且已经接管了原主节点负责的槽
  5. 新的主节点开始接收自己负责处理槽有关的命令请求,故障转移完成。

选主

这个选主方法和哨兵的很相似,两者都是基于 Raft算法 的领头算法实现的。流程如下:

  1. 集群的配置纪元是一个自增计数器,初始值为0;
  2. 当集群里的某个节点开始一次故障转移操作时,集群配置纪元加 1;
  3. 对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,第一个向主节点要求投票的从节点将获得主节点的投票;
  4. 当从节点发现自己复制的主节点进入「已下线」状态时,会向集群广播一条消息,要求收到这条消息,并且具有投票权的主节点为自己投票;
  5. 如果一个主节点具有投票权,且尚未投票给其他从节点,那么该主节点会返回一条消息给要求投票的从节点,表示支持从节点成为新的主节点;
  6. 每个参与选举的从节点会计算获得了多少主节点的支持;
  7. 如果集群中有 N 个具有投票权的主节点,当一个从节点收到的支持票 大于等于 N/2 + 1时,该从节点就会当选为新的主节点;
  8. 如果在一个配置纪元里没有从节点收集到足够多的票数,那么集群会进入一个新的配置纪元,并再次进行选主。

消息

集群中的各个节点通过发送和接收消息来进行通信,我们把发送消息的节点称为发送者,接收消息的称为接收者。

节点发送的消息主要有五种:

  • MEET 메시지
  • PING 메시지
  • PONG 메시지
  • FAIL 메시지
  • PUBLISH 메시지

클러스터의 각 노드는 Gossip 프로토콜, Gossip을 통해 서로 다른 노드의 상태 정보를 교환합니다. MEET, PING, PONG의 세 가지 메시지로 구성됩니다. Gossip 协议交换不同节点的状态信息, Gossip 是由 MEETPINGPONG 三种消息组成。

发送者每次发送 MEETPINGPONG 消息时,都会从自己已知的节点列表中随机选出两个节点(可以是主节点或者从节点)一并发送给接收者。

接收者收到 MEETPINGPONG

발신자가 MEET, PING, PONG 메시지를 보낼 때마다 알려진 노드 목록에서 두 개를 무작위로 선택합니다. (마스터 노드일 수도 있고 슬레이브 노드일 수도 있음)이 수신자에게 함께 전송됩니다.
  • 수신자가 MEET, PINGPONG 메시지를 받으면 다음 두 노드를 알고 있는지 여부에 따라 다른 처리를 수행합니다.
  • 선택한 노드가 존재하지 않고 알려진 노드 목록을 수신 중이라면 이는 첫 번째 접촉이며 수신자는 선택한 노드의 IP 및 포트 번호를 기반으로 통신한다는 의미입니다.

이미 존재하는 경우; , 이는 이전에 통신이 완료되었음을 의미하며 원래 선택한 노드의 정보가 업데이트됩니다.

추천 학습: 🎜Redis 튜토리얼🎜🎜

위 내용은 Redis 고가용성 클러스터를 단계별로 이해해 보세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:csdn.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿