第十章 Redis Cluster集群


什么叫集群??

集群(cluster)技术是一种较新的技术,通过集群技术,可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益,其任务调度则是集群系统中的核心技术。 集群是一组相互独立的、通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理。一个客户与集群相互作用时,集群像是一个独立的服务器。集群配置是用于提高可用性和可缩放性。--------百度百科

Redis Cluster是把这种集群技术运用到Redis数据库中。


这一章主要讲解Redis Cluster的数据分布、节点通信、集群伸缩、请求路由、故障转移、集群运维等几个方面来介绍。

10.1 数据分布


10.1.1 数据分布理论

分布式数据库首先要解决的是如何把整个数据集按照分区规则映射到多个节点的问题。即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。

这里需要重点关注的是数据分区规则。

常见的数据分区规则有哈希分区和顺序分区,如下表所示:

redis和es集群的区别_redis

Redis Cluster采用的是哈希分区规则,接下来介绍常见的哈希分区规则;1 节点取余规则

实现方案是:使用特定的数据,如Redis的键或用户ID,再根据节点数量N使用公式:hash(key)%N计算出哈希值,用来决定映射到哪一个节点上。这方案实现起来比较简单,但是它存在一个问题:当节点数量变化时,数据节点映射关系需要重新计算,这样会导致数据的重新红转移。

redis和es集群的区别_redis_02

2 一致性哈希分区

实现思路:为系统中的每一个节点分配一个token,范围在0~2的32次方,这些token构成一个哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点,这种方式最带的优点是插入删除节点时只影响哈希环中相邻的节点 。但是这种仍然存在很多问题。因此引入了虚拟槽技术对这种方式进行改进。

如下图所示:

redis和es集群的区别_redis_03

3 虚拟槽技术(Redis Cluster采用的集群分区方案)

虚拟槽分区巧妙的使用了哈希空间 ,使用分散度较好的哈希函数把所有的数据映射到一个固定范围的整数集合中,整数定义为槽(slot).

这个范围一般远大于节点数目,比如Redis Cluster的槽范围是0~16383.

槽是集群内数据管理和迁移的单位。采用大范围的槽主要是为了方便数据拆分和集群扩展。


10.1.2 数据分区

Redis Cluster采用虚拟槽技术分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

如图示:

redis和es集群的区别_数据_04

redis和es集群的区别_redis和es集群的区别_05

redis和es集群的区别_redis和es集群的区别_06


10.2 搭建集群

三个步骤: 1)准备节点 2)节点握手 3) 分配槽


10.2.1 准备节点

Redis集群一般有多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。

每个节点需要开启配置cluster-enabled yes.,让redis运行在集群模式下。集群相关配置如下:

redis和es集群的区别_运维_07

节点启动过程如下:

redis和es集群的区别_数据库_08


10.2.2 节点握手

节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。

图示:

redis和es集群的区别_redis和es集群的区别_09

命令cluster meet 127.0.0.1 6380让节点6379和6380节点进行握手通信。cluster meet 是一个异步命令,执行完后立刻返回。6379和6380两个节点通过meet命令建立通信后的集群结构如图10-9.

工作流程如下:

redis和es集群的区别_数据库_10


对节点6379和6380执行cluster nodes命令,可以看到他们已经感受道对方的存在:

redis和es集群的区别_运维_11

我们只需在集群内任意节点上执行cluster meet命令加入新节点,握手状态会通过消息在集群内传播,这样其他节点会自动发现新节点并发起握手流程。最后执行cluster nodes命令确认6个节点都彼此感知并组成集群:

redis和es集群的区别_数据库_12

通过cluster info命令可以获取集群当前状态:

redis和es集群的区别_运维_13

从输出内容上看出:

被分配的槽cluster_slots_assigned是0 ,因为目前所有的槽没有分配到节点,因此集群无法发完成槽到节点的映射。只有当16384个槽全部分配给节点后,集群才进入在线状态。


10.2.3 分配槽

Redis集群把所有的数据映射到16384个槽中。 每个key会映射为一个固定的槽,只有当节点分配了槽,才会相应和这些槽相关的键命令。 通过cluster addslots为节点分配槽。

这里利用bash特性批量设置槽slots :

redis和es集群的区别_redis_14

把16384个slot个槽平均分配给6379 6380 6381三个节点。

执行cluster info查看集群状态:

redis和es集群的区别_数据库_15


作为一个完整的集群,每个负责处理槽的节点应该具有从节点,保证它出现故障时可以自动进行故障转移。

集群模式下,Redis节点角色分为主节点和从节点。

首次启动的节点和被分配的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。

使用cluster replicate {nodeId}让一个节点成为从节点。这个命令必须在对应的从节点上执行,nodeId是要复制的主节点的节点ID :

redis和es集群的区别_数据_16

复制完成后,整个集群结构如下:

redis和es集群的区别_redis和es集群的区别_17

如上,我们是依照Redis协议手动搭建一个集群。该集群由6个节点组成,3个主节点负责处理槽和相关数据,3个从节点负责故障转移。

手动集群比较繁琐,Redis官方提供了redis-trib.rb工具方便我们快速搭建集群。


10.2.4 介绍用redis-trib.rb工具搭建集群

redis-trib.rb

是一个采用Ruby实现的Redis集群管理工具。Ruby是一个依赖环境。 主要是介绍这个工具,不详细展开啦,给出课本上的一些命令。

1 Ruby环境准备

redis和es集群的区别_redis和es集群的区别_18

2 准备节点

redis和es集群的区别_redis和es集群的区别_19

3 创建集群

redis和es集群的区别_redis_20

4 集群完整性检查

redis和es集群的区别_redis_21


10.3 节点通信

10.3.1 通信流程

分布式存储中需要提供维护节点元数据信息的机制。所谓元数据是指:节点负责哪些数据,是否出现故障等状态信心。

常见的元数据维护方式分为:集中式和P2P方式。 person to person 个人对个人

Redis集群采用P2P的Gossip(流言)协议:节点彼此之间不断的通信交换信息,一段时间后所有的节点都会知道集群完整的信息,这种方式类似于流言传播。原理如图所示:

redis和es集群的区别_运维_22

通信过程如下图所示:

redis和es集群的区别_运维_23


10.3.2 Gossip 消息

Gossip 消息的主要职责是信息交换。信息交换的载体就是节点彼此发送的Gossip消息。

常见的Gossip消息可分为:ping 消息 pong 消息 meet消息 fail消息 .他们的通信模式如下:

redis和es集群的区别_数据库_24

redis和es集群的区别_运维_25


所有的消息格式为:消息头和消息体。 消息头包含发送节点自身状态数据,接收节点根据消息头就可以获取到发送节点的相关数据。结构如下:

redis和es集群的区别_数据_26

redis和es集群的区别_数据_27

redis和es集群的区别_数据库_28

redis和es集群的区别_数据库_29


10.3.3 节点选择

Redis集群内节点通信采用固定频率,因此节点每次选择需要通信的节点列表很重要。

Redis内集群通信节点的选择规则如下:

redis和es集群的区别_redis_30


根据通信节点选择的流程可以看出消息交换的成本主要体现在单位时间选择发送消息的节点数量和每个消息携带的数据量。

1 选择发送消息的节点数量

redis和es集群的区别_数据_31

2 消息数据量

redis和es集群的区别_redis和es集群的区别_32


10.4 集群伸缩

10.4.1 伸缩原理

一般Redis集群可以实现对节点的灵活上线下线:其中原理可以抽象为槽和对应数据在不同节点之间灵活移动。

redis和es集群的区别_redis_33

首先 看下之间搭建的集群槽和数据节点的对应关系:

redis和es集群的区别_运维_34

其中三个主节点分别维护自己负责的槽和对应的数据,如果希望加入一个节点实现集群扩容时,需要通过相关命令把一部分槽和数据迁移给新节点,如图所示:

redis和es集群的区别_redis和es集群的区别_35

图中每个节点把一部分槽和数据迁移到新的节点6385,每个节点负责的槽和数据相比之前少了从而达到集群扩容的目的。

可以简单的理解为:

集群伸缩=槽和数据在节点之间的移动


10.4.2 扩容集群 扩容是分布式存储最常见的需求。Redis集群扩容操作可以分为如下几个步骤: 1) 准备新节点 2)加入集群 3)迁移槽和数据


1 准备新节点

新节点建议与集群内的节点配置保持一致 准备号配置后启动两个节点的命令如下:

redis-server  conf/redis-6385.conf
redis-server conf/redis-6386.conf

2 加入集群 新节点依然采用cluster meet命令加入到现有集群中。在集群内任意节点处执行cluster meet让6385和6386两个节点加入 :

cluster meet 127.0.0.1 6385
cluster meet 127.0.0.1 6386

新节点加入集群前后的示意图:

redis和es集群的区别_redis_36

新节点刚开始是主节点状态,但是由于没有负责的槽,所以不能接受任何读写操作,对于新加入的节点的 后续操作一般有两种::

1 为他迁移槽和数据实现扩容

2 作为其他主节点的从节点负责故障转移

注意: 正式环境下建议使用redis-trib.rb add-node 命令加入新节点。一般不选择手动添加的方式,而是利用redis-trib这个工具。


3 迁移槽和数据

加入集群后需要为新节点迁移槽和相关相关数据。 迁移过程是集群扩容中的最核心环节。

1) 槽迁移计划

槽是Redis集群管理数据的基本单位,首先需要为新节点制定槽的迁移计划,确定原有节点的哪些槽需要迁移新节点。

redis和es集群的区别_运维_37

槽迁移计划确定后开始逐个把槽内的数据从源节点迁移到目标节点。

具体流程如图所示

redis和es集群的区别_运维_38


2 迁移数据 数据迁移过程是逐个槽进行的: 流程说明: 1) 对目标节点发送cluster setslot {slot} importing {sourceNodeId}命令,让目标节点准备导入槽的数据 2) 对源节点发送cluster setslot {slot} migrating {targetNodeId},让源节点准备迁出槽的数据 3) 源节点循环执行cluster getkeysinslot {slot} {count}命令。获取count个属于{slot}的槽 4)在源节点上执行migrate {targetIp} {targetPort} "" 0 {timeout} keys {keys...}命令,把获取的键通过流水线机制(pipeline)批量迁移到目标节点。 5)重复执行步骤3 4 直到槽下所有的键值数据迁移到目标节点 6) 向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令通知槽分配给目标节点。

但是一般我们不手动去迁移数据,而是用redis-trib提供的槽重分片功能实现数据的迁移i。 具体操作是如何实现的可以查看课本。


10.4.3 收缩集群

收缩集群意味着缩减规模,需要从现有集群中安全下线部分节点。安全下线节点流程如下图所示:

redis和es集群的区别_运维_39


1 下线迁移槽 下线节点需要自己把自己负责的槽迁移到其他节点,原理与之前节点扩容的迁移槽过程一致。

2 忘记节点

由于集群内的节点不断的通过Gossip消息彼此交换节点状态,因此需要通过一种健壮的机制让集群内所有的节点忘记下线的节点。为此,Redis提供了cluster forget {downNodeId}实现该功能

redis和es集群的区别_数据_40

线上操作不建议直接使用cluster forget执行节点下线,建议使用redis-trib.rb del-node {host:port} {downNodeId}来执行该操作。


10.5 请求路由

10.5.1 请求重定向

集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,如果节点是自身,则处理键命令;否则回复MOVED重定向错误,通知客户端请求正确的节点,这个过程称为MOVED重定向;

如图所示:

redis和es集群的区别_数据_41

键命令执行步骤主要分为两步:计算槽,查找槽所对应的节点。

1 计算槽 Redis首先需要计算键所对应的槽。根据键的有效部分使用CRC16函数计算出散列值,再取对16383的余数,使每个键都可以映射到0-16383槽范围内

2 槽节点查询 Redis计算得到键对应的槽后,需要查找槽所对应的节点。


根据MOVED重定向机制,客户端可以随机连接集群内任一Redis获取键所在的节点,这种客户端叫Dummy客户端,它的代码简单,但是也存在一些问题。因此通常情况下,集群客户端都采用另一种实现:Smart客户端。

10.5.2 Smart客户端 实现原理: Smart客户端通过在内部维护slot->node的映射关系,本地就可以实现键到节点的查找,从而保证IO效率的最大化,而MOVED重定向机制负责协助Smart客户端更新slot->node映射。

10.5.3 ASK重定向


10.6 故障转移

Redis集群内节点通过ping/pong 命令实现节点通信,消息不但可以传播节点槽信息,还可以传播其他状态如:主从状态、节点故障等。因此故障发现也是通过消息传播机制实现的,主要环节如下:主观下线pfail和客观下线fail

再次理解主管下线和客观下线:

主观下线:指某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障判定,只能代表一个节点 的意见,可能存在误判的情况。客观下线:标记一个节点真正的下线,集群内多个节点都认为i该节点不可用,从而达成共识的结果。如果是持有槽的主节点故障,需要为该节点执行故障转移。

Redis集群是比较健壮的故障发现机制,因此只有当认为客观下线后,才认为该节点是真正的下线。


10.6.2 故障恢复

故障节点变为客观下线后,如果该节点是持有槽的主节点,则需要在它的从节点中选出一个替换它,从而保证的集群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主节点进入客观下线后时,将触发故障恢复流程。

故障恢复流程如下:

redis和es集群的区别_数据库_42


10.7 集群运维

10.7.1 集群完整性 为了保证集群的完整性,默认情况下当前集群16384个槽任何一个没有指派到节点时整个集群不可用。


10.7.2 带宽消耗 集群内Gossip消息通信本身会消耗带宽,官方建议集群内最大规模在1000以内,也是出于对消息通信成本的考虑,因此单集群不适合部署超大规模的节点。


10.7.3 Pub/Sub 广播问题 Redis 提供订阅发布功能,用于针对频道实现消息的发布和订阅,但是在集群模式下内部实现对所有的publish命令都会向所有的节点进行广播,造成每条publish数据都会在集群内所有节点传播一次,加重带宽负担。


10.7.4 集群倾斜 集群倾斜指不同节点之间的数据量和请求量出现明显差异,这种情况将加大负载均衡和开发运维的难度。 分为数据倾斜和请求倾斜


10.7.5 集群读写分离

10.7.6 手动故障转移

10.7.7 数据迁移