当前位置:首页>科技 >内容

集群的作用及搭建方法及设计方案怎么写_集群的作用及搭建方法及设计方案

2024-05-08 18:22:49科技漂亮的斑马

上一篇文章已经介绍了Redis的几种高可用技术:持久化、主从复制和哨兵,但是这些方案仍然存在不足,其中最重要的是存储容量受限于单机,无

集群的作用及搭建方法及设计方案怎么写_集群的作用及搭建方法及设计方案

上一篇文章已经介绍了Redis的几种高可用技术:持久化、主从复制和哨兵,但是这些方案仍然存在不足,其中最重要的是存储容量受限于单机,无法实现写操作的负载均衡。

本文将详细介绍该集群,主要内容包括:

集群的作用

集群的构建方法和设计方案

聚类的基本原理

客户端访问集群的方法

实用笔记(集群扩展、故障转移、参数优化等。)

集群的作用

集群,即Redis集群,是Redis 3.0推出的分布式存储方案。一个集群由几个节点组成,Redis的数据分布在这些节点上。

集群中的节点分为主节点和从节点:只有主节点负责读写请求和维护集群信息;从节点只复制主节点的数据和状态信息。

集群的作用可以概括为两点:

数据分区

数据分区(或数据碎片)是集群的核心功能。群集将数据分发到多个节点:

一方面突破了Redis单机内存大小的限制,存储容量大大增加。

另一方面,每个主节点可以对外提供读服务和写服务,大大提高了集群的响应能力。

Redis单机内存大小有限的问题,在持久化和主从复制的介绍中有提到。

例如,如果单个内存过大,bgsave和bgrewriteaof的fork操作可能会导致主进程阻塞,当主机在主从环境中切换时,从节点可能会长时间无法提供服务,主节点的复制缓冲区可能会在完全复制阶段溢出。

高可用性

集群支持主节点的主从复制和自动故障转移(类似于sentry)。当任何一个节点出现故障时,集群仍然可以向外界提供服务。本文基于Redis 3.0.6。

集群的构建

我们将构建一个简单的集群:总共6个节点,3个主节点和3个从节点。为了方便起见,所有节点都在同一个服务器上,并通过端口号进行区分,因此配置很简单。

三个主节点的端口号:7000/7001/7002;对应的从节点端口号:8000/8001/8002。

构建集群有两种方式:

手动执行Redis命令,逐步完成构造。

使用Ruby脚本构建

构建这两者的原理是相同的,只是Ruby脚本打包并封装了Redis命令。实际应用中推荐使用脚本,简单快捷,不易出错。下面分别介绍两种方法。

执行Redis命令来构建集群

集群的构建可以分为四个步骤:

启动节点:以集群模式启动节点,此时节点是独立的,没有建立联系。

节点握手:让独立的节点连接成一个网络。

分配插槽:向主节点分配16,384个插槽。

指定主从关系:为从节点指定主节点。

实际上,前三步完成后,集群就可以对外提供服务了;但是,只有当指定了从属节点时,集群才能提供真正高可用性的服务。

起始节点

集群节点的启动仍然使用redis-server命令,但是需要在集群模式下启动。

以下是7000节点的配置文件(仅列出节点正常运行的关键配置,其他配置如启动AOF可参照单节点进行):

# redis-7000 . conf port 7000 cluster-enabledyescluster-config-file ' node-7000 . conf ' log file ' log-7000 . log ' db filename ' dump-7000 . RDB ' daemonizeyes

集群启用和集群配置文件是与集群相关的配置。

Cluster-enabledyes:Redis: Redis实例可以分为独立模式和集群模式;支持群集的染料可以启动群集模式。

对于以独立模式启动的Redis实例,如果执行info server命令,可以发现redis_mode项是独立的,如下图所示:

对于集群模式的节点,redis_mode是cluster,如下图所示:

Cluster-config-file:该参数指定集群配置文件的位置。在每个节点运行期间,维护一个集群配置文件。

每当集群信息发生变化时(例如添加或删除节点),集群中的所有节点都会将最新信息更新到配置文件中。

当节点重新启动时,它将重新读取配置文件,获取集群信息,并可以轻松地重新加入集群。

换句话说,当Redis节点以集群模式启动时,它将首先查找是否存在集群配置文件。

如果有,使用文件中的配置启动;如果没有,请初始化配置并将其保存到文件中。集群配置文件由Redis节点维护,不需要手动修改。

编辑配置文件后,使用redis-server命令启动节点:

redis-serverredis-7000.conf

节点启动后,可以通过cluster nodes命令查看节点,如下图所示:

返回值中的第一项表示节点id,由40个十六进制字符串组成。节点id不同于文章主从复制中提到的runId。

Redis每次启动都会重新创建runId,但是节点Id只在集群初始化时创建一次,然后保存在集群配置文件中,以后节点重启时会直接读取。

其他节点启动方式相同,这里不再赘述。需要特别注意的是,启动阶段节点之间没有主从关系,所以从节点不需要配备slaveof配置。

节点握手

节点启动后,相互独立,不知道其他节点的存在;节点需要握手,形成一个独立节点的网络。

使用集群meet {ip} {port}命令实现节点握手。例如,如果在7000个节点中执行clustermeet 192.168.72.128 7001,则可以完成7000个节点与7001个节点之间的握手。

注意:ip使用LAN ip而不是localhost或127.0.0.1,这样其他机器上的节点或客户端也可以访问它。

此时,使用集群节点可以查看:

您也可以在7001节点下类似地查看它:

类似地,在7000节点中使用cluster meet命令,可以将所有节点添加到集群中以完成节点握手:

192 . 168 . 72 . 7001集群会议192 . 168 . 72 . 1288001集群会议194

执行上述命令后,您可以看到7000节点已经检测到所有其他节点:

通过节点间的通信,每个节点都可以感知所有其他节点,以8000个节点为例:

分配罐

在Redis集群中,数据分区是通过插槽的方式实现的,具体原理后面会介绍。集群有16,384个插槽,它们是数据管理和迁移的基本单元。

当数据库中的所有16384个插槽都分配了节点后,集群就联机了(OK);如果没有为任何插槽分配节点,群集将处于故障状态。

cluster info命令可以查看群集状态,该状态在分配插槽之前失败:

使用cluster addslots命令分配插槽,并执行以下命令分配所有插槽(编号0-16383):

redis-CLI-p 7000 clusteraddslots { 0.5461 } redis-CLI-p 7001集群添加插槽{5462.10922 } redis-CLI-p 7002集群添加插槽{10923.16383}

此时,检查群集状态,显示所有插槽都已分配,群集已进入在线状态:

指定主从关系

slaveof命令不再用于指定集群中的主从关系,而是使用集群复制命令;参数使用节点id。

通过集群节点获得几个主节点的节点id后,执行以下命令为每个从节点指定主节点:

redis-CLI-p 8000 clusterreplicatebe 816 EBA 968 BC 16 c 884 b 963d 768 c 945 e 86 AC 51 aere dis-CLI-p 8001 cluster replicate 788 b 361563 ABC 175 ce 8232569347812 a 12 f1 FD B4 redis-CLI-p 8002 cluster replicate 26 f 1624 a 3 e 5197 DDE 267 de 683d 61 bb 2 CBD

此时执行cluster nodes检查各个节点的状态,可以看到主从关系已经建立:

至此,集群已经建成。

使用Ruby脚本构建集群

在{REDIS_HOME}/src目录下,可以看到文件redis-trib.rb,这是一个可以实现自动建簇的Ruby脚本。

安装Ruby环境。

以Ubuntu为例,Ruby环境可以安装如下:

Apt-get install Ruby #安装Ruby环境。

Gem install redis #gem是Ruby的包管理工具。这个命令可以安装ruby-redis依赖项。

开始节点

与第一种方法中的“起始节点”完全相同。

建立集群

Redis-trib.rb脚本提供了很多命令,其中create用于构建集群,使用方法如下:/redis-trib . rbcreate-replication 192 . 168 . 72 . 128:7000192 . 168 . 72 . 128:7001192 . 168 . 72 . 128:7002192 . 168 . 72 . 128:8000192 . 168 . 72 . 128:800192 . 168 . 72 . 128:800192 . 168 . 72 . 128:8002

其中:-replicas=1表示每个主节点有一个从节点;后面的{ip:port}表示节点地址,前一个主节点,后一个从节点。使用redis-trib.rb构建集群时,要求节点不能包含任何插槽和数据。

执行create命令后,脚本会给出创建集群的方案,如下图所示;该计划包括哪些是主节点,哪些是从节点,以及如何分配时隙。

输入yes确认执行计划,脚本将按计划执行,如下图所示:

至此,集群已经建成。

集群方案设计

设计集群方案时,至少应考虑以下因素:

高可用性要求:根据故障转移原理,至少需要三个主节点才能完成故障转移,且三个主节点不能在同一台物理机上。

每个主节点至少需要一个从节点,主节点和从节点不应该在同一个物理机上;因此,高可用性集群至少包含6个节点。

数据量和访问量:估算应用需要的数据量和总访问量(考虑业务发展,留有冗余),结合每个主节点的容量和可以承受的访问量(可以通过基准精确估算)计算出需要的主节点数量。

节点数限制:Redis官方给出的节点数限制是1000,主要是考虑节点间通信造成的消耗。

在实际应用中,应尽可能避免大簇。如果节点数量不足以满足应用对Redis数据量和访问量的要求,可以考虑:业务细分,将大集群分成多个小集群;减少不必要的数据;调整数据到期策略等。

适度冗余:Redis可以在不影响集群服务的情况下增加节点,所以节点数量可以适当冗余,不要太多。

聚类的基本原理

以上介绍了集群的构建方法和设计方案,下面将进一步介绍集群的原理。

集群的核心功能是数据分区,所以:

首先,介绍了数据的划分规则。

然后介绍了集群实现的细节:通信机制和数据结构。

最后,以cluster meet(节点握手)和cluster addslots(插槽分配)为例,说明了节点如何利用上述数据结构和通信机制实现集群命令。

数据分区方案

数据划分包括顺序划分和哈希划分,其中哈希划分因其天然的随机性而被广泛使用;簇的划分方案是一种哈希划分。

哈希分区的基本思想是对数据的特征值(比如key)进行哈希,然后根据哈希值决定数据落在哪个节点上。

常见的哈希分区有:哈希余数分区、一致哈希分区、带有虚拟节点的一致哈希分区等。

衡量数据划分方法质量的标准有很多,其中有两个因素比较重要:

数据分布是否均匀。

添加或删除节点对数据分布的影响。

由于哈希的随机性,哈希分区基本可以保证数据分布均匀;因此,在比较哈希分区方案时,重要的是要看增加或减少节点对数据分布的影响。

哈希剩余分区

哈希余数划分的思路很简单:计算key的哈希值,然后余数节点数,确定数据映射到哪个节点。

这种方案最大的问题是在添加或删除节点时,节点数量发生变化,系统中的所有数据都需要重新计算映射关系,导致大规模的数据迁移。

一致散列分区

一致性哈希算法将整个哈希值空间组织成一个虚拟环,如下图所示,范围为0-2 32-1。

对于每一个数据,根据key计算hash值,确定数据在环上的位置,然后从这个位置沿着环顺时针走,找到的第一个服务器就是它应该映射到的服务器。

以上图为例。如果在节点1和节点2之间添加节点5,则只有节点2中的部分数据会迁移到节点5。如果删除节点2,原始节点2中的数据只会迁移到节点4,并且只有节点4会受到影响。

一致哈希分区的主要问题是,当节点数量较少时,增加或删除节点可能会对单个节点产生很大影响,导致数据严重失衡。

以上图为例,如果去掉node2,node4中的数据将从总数据的1/4左右变为1/2左右,相对于其他节点过高。

具有虚拟节点的一致散列分区

该方案在一致哈希划分的基础上,引入了虚拟节点的概念。Redis集群使用这种方案,其中的虚拟节点称为slot。

Slot是数据和实际节点之间的虚拟概念;每个实际节点包含一定数量的槽,每个槽包含具有一定范围内的散列值的数据。

引入槽后,数据的映射关系由数据哈希-实际节点变为数据哈希-槽-实际节点。

在使用槽的一致散列分区中,槽是数据管理和迁移的基本单位。插槽解耦了数据和实际节点之间的关系,添加或删除节点对系统的影响很小。

仍以上图为例,系统中有四个实际节点,假设给它们分配了16个时隙(0-15);插槽0-3位于节点1,插槽4-7位于节点2,依此类推。

如果此时删除node2,只需要重新分配槽4-7,例如槽4-5分配给node1,槽6分配给node3,槽7分配给Node 4;可以看出,删除node2后,数据在其他节点的分布还是比较均衡的。

槽数一般远小于2 ^ 32,比实际节点数大很多。在Redis集群中,槽的数量是16384。

上图很好地总结了将数据映射到Redis集群中实际节点的过程:

Redis计算数据的特征值(通常是key)的哈希值,使用的算法是CRC16。

根据哈希值计算数据属于哪个槽。

根据插槽和节点的映射关系,计算数据属于哪个节点。

节点通信机制

没有节点间的通信,集群就不能作为一个整体工作。

两个端口

在sentinel系统中,节点分为数据节点和哨兵节点:前者存储数据,后者实现附加控制功能。

在集群中,数据节点和非数据节点没有区别:所有节点都存储数据并参与集群状态的维护。

为此,群集中的每个节点都提供了两个TCP端口:

普通端口:即我们前面指定的端口(7000等。).普通端口主要用于为客户端提供服务(类似于单机节点);但是它也将用于节点之间的数据迁移。

集群端口:端口号为10000(10000为固定值,不可更改),例如7000节点的集群端口为17000。

群集端口仅用于节点之间的通信,例如在构建群集、添加或删除节点以及故障转移等操作期间节点之间的通信。不要使用客户端连接到集群接口。为了保证集群能够正常工作,在配置防火墙时,公共端口和集群端口应该同时打开。

八卦协议

节点之间的通信根据通信协议可以分为几种类型:一对一、广播、八卦协议等。重点是广播和八卦的比较。

广播是指向集群中的所有节点发送消息;优点是集群的收敛速度快(集群收敛是指集群中的所有节点都获得相同的集群信息),缺点是每条消息都要发送给所有节点,消耗大量的CPU和带宽。

Gossip协议的特点是在节点数量有限的网络中,每个节点“随机”地与一些节点进行通信(并不是真的随机,而是按照特定的规则选择通信的节点)。经过一番混沌通信,各个节点的状态很快就会达到一致。

Gossip协议具有低负载(比广播低)、去中心化、高容错性(因为通信是冗余的)等优点。主要缺点是集群收敛速度慢。

消息类型

集群中的节点以固定的频率(每秒10次)执行与通信相关的任务,如判断是否需要发送消息和消息类型、确定接收节点、发送消息等。

如果集群的状态发生变化,比如增加或删除节点,改变插槽的状态,所有节点都会通过节点间的通信快速知道整个集群的状态,从而使集群收敛。

节点间发送的消息主要分为五种类型:

会议消息

PING消息

乒乓消息

失败消息

发布消息

不同的消息类型、通信协议、发送的频率和定时、接收节点的选择等。是不同的:

相遇消息:在节点握手阶段,节点收到客户端的集群相遇命令时,会向新加入的节点发送相遇消息,请求新节点加入当前集群;新节点将在接收到会议消息后回复PONG消息。

PING报文:集群中的每个节点每秒会选择一些节点发送一个PING报文,接收方收到报文后会回复一个PONG报文。PING报文的内容是自身节点和其他一些节点的状态信息;功能是互相交换信息,检测节点是否在线。

PING报文采用Gossip协议发送,接收节点的选择考虑了收敛速度和带宽开销。具体规则如下:随机找5个节点,选择最长时间没有通信的节点。(2)扫描节点列表,选择所有最晚接收PONG消息时间大于cluster_node_timeout/2的节点,防止这些节点长时间更新。

PONG消息:PONG消息封装了自己的状态数据。可以分为两种:第一种是收到MEET/PING消息后回复的PONG消息;第二种是节点向集群广播PONG消息。

这样,其他节点就可以知道该节点的最新信息,比如新的主节点在故障恢复后会广播一个PONG消息。

失败消息:当一个主节点判断另一个主节点已经进入失败状态时,它会向集群广播这个失败消息;接收节点将保存该失败消息用于后续判断。

发布消息:节点收到发布命令后,会先执行命令,然后向集群广播消息,接收节点也会执行发布命令。

数据结构

节点需要特殊的数据结构来存储集群的状态。所谓集群状态是一个很大的概念,包括:集群是否在线,集群中有哪些节点,节点是否可达,节点的主从状态,插槽的分布.

在节点提供的存储集群状态的数据结构中,最关键的是clusterNode和集群状态结构:前者记录一个节点的状态,后者记录整个集群的状态。

集群节点

ClusterNode结构保存节点的当前状态,包括创建时间、节点id、ip和端口号。

每个节点将使用一个clusterNode结构来记录自己的状态,并为集群中的所有其他节点创建一个clusterNode结构来记录节点状态。

下面列出了clusterNode的一些字段,并解释了它们的含义和功能:

TypedefstructclusterNode{//节点创建时间mstime _ tctime//node idcharname[redis _ cluster _ namelen];//节点charip的ip和端口号[REDIS _ IP _ STR _ LEN];intport//节点ID:整数,每一位代表不同的状态,比如节点的主从状态,是否在线,是否握手等。//配置纪元:在故障转移时工作,类似哨兵的配置纪元uint64 _ tconfigEpoch//本节点槽位分布:占用16384/8字节,16384位;每个比特对应一个槽:如果比特值为1,则该比特对应的槽在节点中;如果位值为0,则该位对应的槽不在节点unsignedcharslots[16384/8]中;//节点intnumslots中的槽数;…………} cluster node;

此外,clusterState还包括所需的信息,如故障转移和插槽迁移。

集群命令的实现

本部分将以cluster meet(节点握手)和cluster addslots(插槽分配)为例,说明节点如何利用上述数据结构和通信机制实现集群命令。

集群会议

假设向节点A发送一个集群会议命令,将节点B加入到A所在的集群中。收到命令后,节点A执行以下操作:

a为b创建一个clusterNode结构,并将其添加到clusterState的节点字典中。

a向b发送会议消息。

收到MEET消息后,B将为A创建一个clusterNode结构,并将其添加到clusterState的节点字典中。

b用PONG消息回复A。

在收到来自B的PONG消息后,A知道B已经成功接收了自己的MEET消息。

然后,A向b返回PING消息。

B收到A的PING报文后,知道A已经成功收到自己的PONG报文,握手完成。

之后,A通过Gossip协议将B的信息广播给集群中的其他节点,其他节点会与B握手;一段时间后,集群收敛,B成为集群中的普通节点。

通过上面的过程可以发现,集群中两个节点之间的握手过程类似于TCP,都是三次握手:A发送一个MEETto B;b发PONG到a;a向b发送PING。

集群添加插槽

集群中插槽的分配信息存储在clusterNode的插槽数组和clusterState的插槽数组中。前面已经介绍了这两个数组的结构。

两者的区别在于前者存储的是本节点分配了哪些槽,后者存储的是集群中所有槽分布在哪个节点。

cluster addslots命令接收一个或多个插槽作为参数。例如,执行群集添加插槽{0.10}节点A上的命令将编号为0-10的插槽分配给节点A.

具体实施过程如下:

遍历输入槽,检查它们是否都已分配。如果分配了一个插槽,命令执行将失败。方法是检查clusterState.slots[]中输入槽的对应值是否为空。

遍历输入槽并将其分配给节点a;方法是将clusterNode.slots[]中对应的位修改为1,clusterState.slots[]中对应的指针指向节点A.

节点A执行完成后,会通过节点通信机制通知其他节点,所有节点都会知道0-10个时隙分配给了节点A.

客户端访问群集

在一个集群中,数据分布在不同的节点上,当一个客户端通过一个节点访问数据时,数据可能不在那个节点上;下面是集群如何处理这个问题。

redis-cli

当节点从redis-cli收到命令(如set/get)时,流程如下:

计算钥匙属于哪个槽:CRC16(钥匙)16383。

集群提供的集群keyslot命令也是利用上述公式实现的,比如:

判断键所在的槽是否在当前节点:假设键在第I个槽,clusterState.slots[i]指向槽所在的节点。

如果clusterstate。插槽[I]==集群状态。我自己,意思是槽在当前节点,命令可以直接在当前节点执行。

否则,这意味着该槽不在当前节点中,并且地址(clusterState.slots[i])。ip/port)并打包成一个移动错误返回给redis-cli。

redis-cli收到移动错误后,根据返回的ip和端口重新发送请求。

下面的例子展示了redis-cli和cluster的交互过程:key1在节点7000操作,但是key1所在的槽9189在节点7001。

因此,节点向redis-cli返回一个移动错误(包括节点7001的ip和端口),redis-cli重新向7001发起请求。

在上面的示例中,redis-Cli通过-c指定了集群模式。如果未指定,redis-cli将无法处理移动的错误:

智能客户端

redis-cli等客户端被称为伪客户端,因为它们在执行命令之前不知道数据在哪个节点,需要借助移动的错误进行重定向。与虚拟客户端相对应的是智能客户端。

智能客户端(以Java的JedisCluster为例)的基本原理如下:

(1)初始化JedisCluster时,通过连接任意节点并执行cluster slots命令,在内部维护槽节点缓存,返回如下:

此外,JedisCluster为每个节点创建一个连接池(即JedisPool)。

执行命令时,JedisCluster根据key-slot-node选择要连接的节点,并发送命令。

如果成功,则执行该命令;如果执行失败,将随机选择其他节点重试,当发生移动错误时,将使用集群插槽来重新同步插槽-节点映射关系。

下面的代码演示了如何使用JedisCluster访问集群(不考虑资源释放、异常处理等)。):

publistaticvoidtest(){ set nodes=new hashset();nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '7000));nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '7001));nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '7002));nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '8000));nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '8001));nodes . add(newHostAndPort(' 192 . 168 . 72 . 128 '8002));JedisClustercluster=new jediscluster(节点);system . out . println(cluster . get(' key 1 '));cluster . close();}

注意事项如下:

JedisCluster已经包含了所有节点的连接池,所以JedisCluster应该使用singletons。

客户端维护插槽-节点映射关系,并为每个节点创建一个连接池。当节点数量较大时,要注意客户端的内存资源和连接资源的消耗。

较新版本的Jedis对JedisCluster做了一些性能优化,比如集群槽缓存更新和锁阻塞,所以尽量使用Jedis 2 . 8 . 2及以上版本。

实用说明

前面介绍了集群的正常操作和访问的方法和原理,下面是一些重要的补充内容。

集群扩展

在实践中,经常需要对集群进行伸缩,比如当访问次数增加时的扩展操作。Redis集群可以在不影响外部服务的情况下进行扩展;伸缩的核心是槽迁移:修改槽与节点的对应关系,实现槽(即数据)在节点间的移动。

例如,如果插槽均匀分布在集群的三个节点中,此时增加一个节点,则需要从三个节点中取出一些插槽到新节点,以实现插槽在四个节点中的均匀分布。

添加节点

假设添加7003和8003节点,其中8003是7003的从节点,步骤如下:

启动节点:方法参见集群构建。

节点握手:可以使用cluster meet命令,但建议在生产环境中使用redis-trib.rb的add-node工具。它的原理也是cluster meet,但是它会先检查新节点是否加入了其他集群或者有数据,避免加入集群后混淆。

redis-trib . rbadd-node 192 . 168 . 72 . 128:7003192 . 168 . 72 . 1287000

迁移槽:建议使用redis-trib的reshard工具,rb刷新自动化程度高,只需要输入redis-trib.rb reshard ip:port (ip和port可以是集群中的任意节点)。

然后按照提示输入以下信息,插槽迁移将自动完成:

要迁移的插槽数量:16,384个插槽平均分配给4个节点,每个节点有4,096个插槽,因此要迁移的插槽数量为4,096个。

目标节点id:7003节点的id。

源节点Id:7000/7001/7002节点的ID。

指定主从关系:方法参见集群构建。

减少节点

假设您想使7000/8000节点离线,这可以分为两步:

迁移插槽:使用reshard将7000节点中的插槽均匀地迁移到7001/7002/7003节点。

离线节点:使用redis-trib.rb del-node工具;从节点应该先脱机,再脱机主节点,因为如果主节点先脱机,从节点会被指向其他主节点,导致不必要的全面复制。

Redis-trib . rbdel-node 192 . 168 . 72 . 128:7001 {节点8000的ID }

询问错误

集群扩展的核心是插槽迁移。在插槽迁移过程中,如果客户端向源节点发送命令,则源节点执行以下过程:

接收到ASK错误后,客户端读取目标节点的地址信息,并向目标节点重新发送请求,就像接收移动错误时一样。

但是两者有很大的区别:ASK error表示数据正在迁移,不知道什么时候迁移完成,所以重定向是临时的,智能客户端不会刷新slots缓存;移动错误重定向是(相对)永久的,智能客户端将刷新插槽缓存。

故障切换

在文章“Sentinel”中,介绍了Sentinel的故障发现和故障转移原理。

虽然细节上差别很大,但集群的实现和哨兵类似:PING消息由调度任务发送,检测其他节点的状态;节点下线分为主观下线和客观下线;客观下线后选择一个从节点进行故障转移。

和哨兵一样,集群只实现主节点的故障转移;当从节点出现故障时,它只会脱机,不会进行故障转移。

因此,在使用集群时,要慎重使用读写分离技术,因为从节点的失效会导致读服务不可用,可用性差。

这里不详细介绍故障转移的细节,只说明重要的事项:

节点数量:在故障转移阶段,主节点需要投票决定哪个从节点成为新的主节点;从节点选举中胜出所需票数为n/21;其中n是主节点的数量(包括失败的主节点),但是失败的主节点实际上不能投票。

因此,为了在故障发生时成功选择从节点,集群中至少需要三个主节点(并且部署在不同的物理机上)。

故障转移时间:主节点故障到转移完成的时间主要消耗在主观离线识别、主观离线传播、选举延迟等几个环节。

具体时间与参数cluster-node-timeout有关。一般来说:

故障转移时间(毫秒)1.5 *群集节点超时1000。

群集节点超时的默认值为15000毫秒(15秒),因此故障转移时间大约为20秒。

集群的局限性及其对策

因为群集中的数据分布在不同的节点上,所以一些功能受到限制,包括:

密钥批处理操作受到限制:例如,mget和mset操作只能在操作的密钥都位于一个槽中时执行。

为了解决这个问题,一种思路是在客户端记录槽和键的信息,并执行mget/mset;每次用于特定的时隙;另一个想法是使用散列标签。

Keys/flushall等操作:keys/flushall等操作可以在任何节点上执行,但结果只针对当前节点。例如,keys操作只返回当前节点的所有键。

为了解决这个问题,我们可以使用集群节点来获取客户端的所有节点信息,并在所有主节点上执行keys/flushall操作。

Transaction /Lua脚本:集群支持Transaction和Lua脚本,但前提是涉及的键必须在同一个节点。哈希标签可以解决这个问题。

数据库:一个独立的Redis节点可以支持16个数据库,在集群模式下只能支持一个数据库,即db0。

复制结构:只支持一层复制结构,不支持嵌套。

哈希标签

Hash标签原理是:当一个键包含{}时,不哈希整个键,只哈希{}中包含的字符串。

Hash标签可以使不同的键有相同的hash值,可以分布在同一个槽中;这样,对不同的键(mget/mset等)进行批量操作。),以及事务和Lua脚本都可以支持。

但是Hash标签可能会带来数据分布不均匀的问题。这时,有必要:

调整不同节点的槽数,使数据分布尽可能均匀。

避免对热数据使用散列标签,这会导致请求分布不均匀。

下面是一个使用Hash标签的例子:通过给产品添加Hash标签,可以将所有的产品信息放在同一个槽位,方便操作。

参数最优化

群集节点超时

初步引入了cluster_node_timeout参数;其默认值为15s,其效果包括:

影响PING报文接收节点的选择:值越大,延迟容忍度越高,选择的接收节点越少,可以减少带宽,但会减慢收敛速度;它应该根据带宽和应用要求进行调整。

影响故障转移的判断和时间:值越大越不容易误判,但完成故障转移的时间越长;应根据网络条件和应用要求进行调整。

集群要求全覆盖

如前所述,只有当分配完所有16,384个插槽时,集群才能联机。这样做是为了确保集群的完整性。

但同时也带来了新的问题:当主节点出现故障,故障转移还没有完成,而原来主节点中的槽不在任何节点中时,集群就会离线,无法响应客户端的请求。

cluster-require-full-coverage参数可以更改此设置:如果将其设置为no,则当插槽未完全分配时,群集仍然可以联机。

该参数的默认值是yes。如果应用程序需要高可用性,可以将其更改为no,但是您需要确保所有的插槽都已分配。

redis-trib.rb

Redis-trib.rb提供了许多实用的工具:创建集群、添加或删除节点、迁移插槽、检查完整性、重新平衡数据等。您可以通过help命令查看详细信息。

在实际操作中,如果能使用redis-trib.rb这个工具,就尽量使用,这样不仅方便快捷,还能大大降低出错概率。

声明本站所有作品图文均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系我们

Top