java 培训 redis 集群原理详解
一、Redis 集群介绍:
1、为什么需要 Redis 集群?
在讲 Redis 集群架构之前,我们先简单讲下 Redis 单实例的架构,从最开始的一主 N 从,到读写分离,再到 Sentinel 哨兵机制,单实例的 Redis 缓存足以应对大多数的使用场景,也能实现主从故障迁移。
编辑
但是,在某些场景下,单实例存 Redis 缓存会存在的几个问题:
(1)写并发:
Redis 单实例读写分离可以解决读操作的负载均衡,但对于写操作,仍然是全部落在了 master 节点上面,在海量数据高并发场景,一个节点写数据容易出现瓶颈,造成 master 节点的压力上升。
(2)海量数据的存储压力:
单实例 Redis 本质上只有一台 Master 作为存储,如果面对海量数据的存储,一台 Redis 的服务器就应付不过来了,而且数据量太大意味着持久化成本高,严重时可能会阻塞服务器,造成服务请求成功率下降,降低服务的稳定性。
针对以上的问题,Redis 集群提供了较为完善的方案,解决了存储能力受到单机限制,写操作无法负载均衡的问题。
更多 Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
2、什么是 Redis 集群?
Redis3.0 加入了 Redis 的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的 master 节点上面,从而解决了海量数据的存储问题。
Redis 集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一 Redis 实例一样,不需要任何代理中间件,当客户端操作的 key 没有分配到该 node 上时,Redis 会返回转向指令,指向正确的 node_java培训。
Redis 也内置了高可用机制,支持 N 个 master 节点,每个 master 节点都可以挂载多个 slave 节点,当 master 节点挂掉时,集群会提升它的某个 slave 节点作为新的 master 节点。
编辑
如上图所示,Redis 集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有 master 节点具有处理请求的能力,slave 节点主要是用于节点的高可用)
二、Redis 集群的数据分布算法:哈希槽算法
1、什么是哈希槽算法?
前面讲到,Redis 集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的 Redis 服务器上。常见的分区算法有 hash 算法、一致性 hash 算法,关于这些算法这里就不多介绍。
普通 hash 算法:将 key 使用 hash 算法计算之后,按照节点数量来取余,即 hash(key)%N。优点就是比较简单,但是扩容或者摘除节点时需要重新根据映射关系计算,会导致数据重新迁移。
一致性 hash 算法:为每一个节点分配一个 token,构成一个哈希环;查找时先根据 key 计算 hash 值,然后顺时针找到第一个大于等于该哈希值的 token 节点。优点是在加入和删除节点时只影响相邻的两个节点,缺点是加减节点会造成部分数据无法命中,所以一般用于缓存,而且用于节点量大的情况下,扩容一般增加一倍节点保障数据负载均衡。
Redis 集群采用的算法是哈希槽分区算法。Redis 集群中有 16384 个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的 Redis 节点上面进行管理,也就是说每个 Redis 节点只负责一部分的哈希槽。在对数据进行操作的时候,集群会对使用 CRC16 算法对 key 进行计算并对 16384 取模(slot = CRC16(key)%16383),得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的 Redis 节点,然后直接到这个对应的节点上进行存取操作。
使用哈希槽的好处就在于可以方便的添加或者移除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;哈希槽数据分区算法具有以下几种特点:
解耦数据和节点之间的关系,简化了扩容和收缩难度;
节点自身维护槽的映射关系,不需要客户端代理服务维护槽分区元数据
支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景
槽的迁移与指派命令:CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
默认情况下,redis 集群的读和写都是到 master 上去执行的,不支持 slave 节点读和写,跟 Redis 主从复制下读写分离不一样,因为 redis 集群的核心的理念,主要是使用 slave 做数据的热备,以及 master 故障时的主备切换,实现高可用的。Redis 的读写分离,是为了横向任意扩展 slave 节点去支撑更大的读吞吐量。而 redis 集群架构下,本身 master 就是可以任意扩展的,如果想要支撑更大的读或写的吞吐量,都可以直接对 master 进行横向扩展。
2、Redis 中哈希槽相关的数据结构:
(1)clusterNode 数据结构:保存节点的当前状态,比如节点的创建时间,节点的名字,节点当前的配置纪元,节点的 IP 和地址,等等。
编辑
(2)clusterState 数据结构:记录当前节点所认为的集群目前所处的状态。
编辑
(3)节点的槽指派信息:
clusterNode 数据结构的 slots 属性和 numslot 属性记录了节点负责处理那些槽:
编辑
slots 属性是一个二进制位数组(bit array),这个数组的长度为 16384/8=2048 个字节,共包含 16384 个二进制位。Master 节点用 bit 来标识对于某个槽自己是否拥有,时间复杂度为 O(1)
(4)集群所有槽的指派信息:
当收到集群中其他节点发送的信息时,通过将节点槽的指派信息保存在本地的 clusterState.slots 数组里面,程序要检查槽 i 是否已经被指派,又或者取得负责处理槽 i 的节点,只需要访问 clusterState.slots[i]的值即可,时间复杂度仅为 O(1)
编辑
如上图所示,ClusterState 中保存的 Slots 数组中每个下标对应一个槽,每个槽信息中对应一个 clusterNode 也就是缓存的节点。这些节点会对应一个实际存在的 Redis 缓存服务,包括 IP 和 Port 的信息。Redis Cluster 的通讯机制实际上保证了每个节点都有其他节点和槽数据的对应关系。无论 Redis 的客户端访问集群中的哪个节点都可以路由到对应的节点上,因为每个节点都有一份 ClusterState,它记录了所有槽和节点的对应关系。
3、集群的请求重定向:
前面讲到,Redis 集群在客户端层面没有采用代理,并且无论 Redis 的客户端访问集群中的哪个节点都可以路由到对应的节点上,下面来看看 Redis 客户端是如何通过路由来调用缓存节点的:
(1)MOVED 请求:
编辑
如上图所示,Redis 客户端通过 CRC16(key)%16383 计算出 Slot 的值,发现需要找“缓存节点 1”进行数据操作,但是由于缓存数据迁移或者其他原因导致这个对应的 Slot 的数据被迁移到了“缓存节点 2”上面。那么这个时候 Redis 客户端就无法从“缓存节点 1”中获取数据了。但是由于“缓存节点 1”中保存了所有集群中缓存节点的信息,因此它知道这个 Slot 的数据在“缓存节点 2”中保存,因此向 Redis 客户端发送了一个 MOVED 的重定向请求。这个请求告诉其应该访问的“缓存节点 2”的地址。Redis 客户端拿到这个地址,继续访问“缓存节点 2”并且拿到数据。
(2)ASK 请求:
上面的例子说明了,数据 Slot 从“缓存节点 1”已经迁移到“缓存节点 2”了,那么客户端可以直接找“缓存节点 2”要数据。那么如果两个缓存节点正在做节点的数据迁移,此时客户端请求会如何处理呢?
编辑
Redis 客户端向“缓存节点 1”发出请求,此时“缓存节点 1”正向“缓存节点 2”迁移数据,如果没有命中对应的 Slot,它会返回客户端一个 ASK 重定向请求并且告诉“缓存节点 2”的地址。客户端向“缓存节点 2”发送 Asking 命令,询问需要的数据是否在“缓存节点 2”上,“缓存节点 2”接到消息以后返回数据是否存在的结果。
更多 Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
(3)频繁重定向造成的网络开销的处理:smart 客户端
① 什么是 smart 客户端:
在大部分情况下,可能都会出现一次请求重定向才能找到正确的节点,这个重定向过程显然会增加集群的网络负担和单次请求耗时。所以大部分的客户端都是 smart 的。所谓 smart 客户端,就是指客户端本地维护一份 hashslot => node 的映射表缓存,大部分情况下,直接走本地缓存就可以找到 hashslot => node,不需要通过节点进行 moved 重定向,
② JedisCluster 的工作原理:
在 JedisCluster 初始化的时候,就会随机选择一个 node,初始化 hashslot => node 映射表,同时为每个节点创建一个 JedisPool 连接池。
每次基于 JedisCluster 执行操作时,首先会在本地计算 key 的 hashslot,然后在本地映射表找到对应的节点 node。
如果那个 node 正好还是持有那个 hashslot,那么就 ok;如果进行了 reshard 操作,可能 hashslot 已经不在那个 node 上了,就会返回 moved。
如果 JedisCluter API 发现对应的节点返回 moved,那么利用该节点返回的元数据,更新本地的 hashslot => node 映射表缓存
重复上面几个步骤,直到找到对应的节点,如果重试超过 5 次,那么就报错,JedisClusterMaxRedirectionException
③ hashslot 迁移和 ask 重定向:
如果 hashslot 正在迁移,那么会返回 ask 重定向给客户端。客户端接收到 ask 重定向之后,会重新定位到目标节点去执行,但是因为 ask 发生在 hashslot 迁移过程中,所以 JedisCluster API 收到 ask 是不会更新 hashslot 本地缓存。
虽然 ASK 与 MOVED 都是对客户端的重定向控制,但是有本质区别。ASK 重定向说明集群正在进行 slot 数据迁移,客户端无法知道迁移什么时候完成,因此只能是临时性的重定向,客户端不会更新 slots 缓存。但是 MOVED 重定向说明键对应的槽已经明确指定到新的节点,客户端需要更新 slots 缓存。
三、Redis 集群中节点的通信机制:goosip 协议
redis 集群的哈希槽算法解决的是数据的存取问题,不同的哈希槽位于不同的节点上,而不同的节点维护着一份它所认为的当前集群的状态,同时,Redis 集群是去中心化的架构。那么,当集群的状态发生变化时,比如新节点加入、slot 迁移、节点宕机、slave 提升为新 Master 等等,我们希望这些变化尽快被其他节点发现,Redis 是如何进行处理的呢?也就是说,Redis 不同节点之间是如何进行通信进行维护集群的同步状态呢?
在 Redis 集群中,不同的节点之间采用 gossip 协议进行通信,节点之间通讯的目的是为了维护节点之间的元数据信息。这些元数据就是每个节点包含哪些数据,是否出现故障,通过 gossip 协议,达到最终数据的一致性。
gossip 协议,是基于流行病传播方式的节点或者进程之间信息交换的协议。原理就是在不同的节点间不断地通信交换信息,一段时间后,所有的节点就都有了整个集群的完整信息,并且所有节点的状态都会达成一致。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,但只要这些节可以通过网络连通,最终他们的状态就会是一致的。Gossip 协议最大的好处在于,即使集群节点的数量增加,每个节点的负载也不会增加很多,几乎是恒定的。
Redis 集群中节点的通信过程如下:
集群中每个节点都会单独开一个 TCP 通道,用于节点间彼此通信。
每个节点在固定周期内通过待定的规则选择几个节点发送 ping 消息
接收到 ping 消息的节点用 pong 消息作为响应
使用 gossip 协议的优点在于将元数据的更新分散在不同的节点上面,降低了压力;但是缺点就是元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。另外,由于 gossip 协议对服务器时间的要求较高,时间戳不准确会影响节点判断消息的有效性。而且节点数量增多后的网络开销也会对服务器产生压力,同时结点数太多,意味着达到最终一致性的时间也相对变长,因此官方推荐最大节点数为 1000 左右。
redis cluster 架构下的每个 redis 都要开放两个端口号,比如一个是 6379,另一个就是加 1w 的端口号 16379。
6379 端口号就是 redis 服务器入口。
16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用的是一种叫 gossip 协议的二进制协议
1、gossip 协议的常见类型:
gossip 协议常见的消息类型包含:ping、pong、meet、fail 等等。
(1)meet:主要用于通知新节点加入到集群中,通过「cluster meet ip port」命令,已有集群的节点会向新的节点发送邀请,加入现有集群。
(2)ping:用于交换节点的元数据。每个节点每秒会向集群中其他节点发送 ping 消息,消息中封装了自身节点状态还有其他部分节点的状态数据,也包括自身所管理的槽信息等等。
因为发送 ping 命令时要携带一些元数据,如果很频繁,可能会加重网络负担。因此,一般每个节点每秒会执行 10 次 ping,每次会选择 5 个最久没有通信的其它节点。
如果发现某个节点通信延时达到了 cluster_node_timeout / 2,那么立即发送 ping,避免数据交换延时过长导致信息严重滞后。比如说,两个节点之间都 10 分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况,就会有问题。所以 cluster_node_timeout 可以调节,如果调得比较大,那么会降低 ping 的频率。
每次 ping,会带上自己节点的信息,还有就是带上 1/10 其它节点的信息,发送出去,进行交换。至少包含 3 个其它节点的信息,最多包含 (总节点数 - 2)个其它节点的信息。
(3)pong:ping 和 meet 消息的响应,同样包含了自身节点的状态和集群元数据信息。
(4)fail:某个节点判断另一个节点 fail 之后,向集群所有节点广播该节点挂掉的消息,其他节点收到消息后标记已下线。
由于 Redis 集群的去中心化以及 gossip 通信机制,Redis 集群中的节点只能保证最终一致性。例如当加入新节点时(meet),只有邀请节点和被邀请节点知道这件事,其余节点要等待 ping 消息一层一层扩散。除了 Fail 是立即全网通知的,其他诸如新节点、节点重上线、从节点选举成为主节点、槽变化等,都需要等待被通知到,也就是 Gossip 协议是最终一致性的协议。
2、meet 命令的实现:
编辑
(1)节点 A 会为节点 B 创建一个 clusterNode 结构,并将该结构添加到自己的 clusterState.nodes 字典里面。
(2)节点 A 根据 CLUSTER MEET 命令给定的 IP 地址和端口号,向节点 B 发送一条 MEET 消息。
(3)节点 B 接收到节点 A 发送的 MEET 消息,节点 B 会为节点 A 创建一个 clusterNode 结构,并将该结构添加到自己的 clusterState.nodes 字典里面。
(4)节点 B 向节点 A 返回一条 PONG 消息。
(5)节点 A 将受到节点 B 返回的 PONG 消息,通过这条 PONG 消息,节点 A 可以知道节点 B 已经成功的接收了自己发送的 MEET 消息。
(6)之后,节点 A 将向节点 B 返回一条 PING 消息。
(7)节点 B 将接收到的节点 A 返回的 PING 消息,通过这条 PING 消息节点 B 可以知道节点 A 已经成功的接收到了自己返回的 PONG 消息,握手完成。
(8)之后,节点 A 会将节点 B 的信息通过 Gossip 协议传播给集群中的其他节点,让其他节点也与节点 B 进行握手,最终,经过一段时间后,节点 B 会被集群中的所有节点认识。
四、集群的扩容与收缩:
作为分布式部署的缓存节点总会遇到缓存扩容和缓存故障的问题。这就会导致缓存节点的上线和下线的问题。由于每个节点中保存着槽数据,因此当缓存节点数出现变动时,这些槽数据会根据对应的虚拟槽算法被迁移到其他的缓存节点上。所以对于 redis 集群,集群伸缩主要在于槽和数据在节点之间移动。
1、扩容:
(1)启动新节点
(2)使用 cluster meet 命令将新节点加入到集群
(3)迁移槽和数据:添加新节点后,需要将一些槽和数据从旧节点迁移到新节点
编辑
如上图所示,集群中本来存在“缓存节点 1”和“缓存节点 2”,此时“缓存节点 3”上线了并且加入到集群中。此时根据虚拟槽的算法,“缓存节点 1”和“缓存节点 2”中对应槽的数据会应该新节点的加入被迁移到“缓存节点 3”上面。
新节点加入到集群的时候,作为孤儿节点是没有和其他节点进行通讯的。因此需要在集群中任意节点执行 cluster meet 命令让新节点加入进来。假设新节点是 192.168.1.1 5002,老节点是 192.168.1.1 5003,那么运行以下命令将新节点加入到集群中。
192.168.1.1 5003> cluster meet 192.168.1.1 5002
这个是由老节点发起的,有点老成员欢迎新成员加入的意思。新节点刚刚建立没有建立槽对应的数据,也就是说没有缓存任何数据。如果这个节点是主节点,需要对其进行槽数据的扩容;如果这个节点是从节点,就需要同步主节点上的数据。总之就是要同步数据。
编辑
更多 Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
如上图所示,由客户端发起节点之间的槽数据迁移,数据从源节点往目标节点迁移。
(1)客户端对目标节点发起准备导入槽数据的命令,让目标节点准备好导入槽数据。这里使用 cluster setslot {slot} importing {sourceNodeId} 命令。
(2)之后对源节点发起送命令,让源节点准备迁出对应的槽数据。使用命令 cluster setslot {slot} importing {sourceNodeId}。
(3)此时源节点准备迁移数据了,在迁移之前把要迁移的数据获取出来。通过命令 cluster getkeysinslot {slot} {count}。Count 表示迁移的 Slot 的个数。
(4)然后在源节点上执行,migrate {targetIP} {targetPort} “” 0 {timeout} keys {keys} 命令,把获取的键通过流水线批量迁移到目标节点。
(5)重复 3 和 4 两步不断将数据迁移到目标节点。
(6)完成数据迁移到目标节点以后,通过 cluster setslot {slot} node {targetNodeId} 命令通知对应的槽被分配到目标节点,并且广播这个信息给全网的其他主节点,更新自身的槽节点对应表。
2、收缩:
迁移槽。
忘记节点。通过命令 cluster forget {downNodeId} 通知其他的节点
编辑
为了安全删除节点,Redis 集群只能下线没有负责槽的节点。因此如果要下线有负责槽的 master 节点,则需要先将它负责的槽迁移到其他节点。迁移的过程也与上线操作类似,不同的是下线的时候需要通知全网的其他节点忘记自己,此时通过命令 cluster forget {downNodeId} 通知其他的节点。
五、集群的故障检测与故障转恢复机制:
1、集群的故障检测:
Redis 集群的故障检测是基于 gossip 协议的,集群中的每个节点都会定期地向集群中的其他节点发送 PING 消息,以此交换各个节点状态信息,检测各个节点状态:在线状态、疑似下线状态 PFAIL、已下线状态 FAIL。
(1)主观下线(pfail):当节点 A 检测到与节点 B 的通讯时间超过了 cluster-node-timeout 的时候,就会更新本地节点状态,把节点 B 更新为主观下线。
主观下线并不能代表某个节点真的下线了,有可能是节点 A 与节点 B 之间的网络断开了,但是其他的节点依旧可以和节点 B 进行通讯。
(2)客观下线:
由于集群内的节点会不断地与其他节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点,因此集群内的节点会不断收到下线报告。
当半数以上的主节点标记了节点 B 是主观下线时,便会触发客观下线的流程(该流程只针对主节点,如果是从节点就会忽略)。将主观下线的报告保存到本地的 ClusterNode 的结构 fail_reports 链表中,并且对主观下线报告的时效性进行检查,如果超过 cluster-node-timeout*2 的时间,就忽略这个报告,否则就记录报告内容,将其标记为客观下线。
接着向集群广播一条主节点 B 的 Fail 消息,所有收到消息的节点都会标记节点 B 为客观下线。
2、集群地故障恢复:
当故障节点下线后,如果是持有槽的主节点则需要在其从节点中找出一个替换它,从而保证高可用。此时下线主节点的所有从节点都担负着恢复义务,这些从节点会定时监测主节点是否进入客观下线状态,如果是,则触发故障恢复流程。故障恢复也就是选举一个节点充当新的 master,选举的过程是基于 Raft 协议选举方式来实现的。
2.1、从节点过滤:
检查每个 slave 节点与 master 节点断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成 master
2.2、投票选举:
(1)节点排序:
对通过过滤条件的所有从节点进行排序,按照 priority、offset、run id 排序,排序越靠前的节点,越优先进行选举。
priority 的值越低,优先级越高
offset 越大,表示从 master 节点复制的数据越多,选举时间越靠前,优先进行选举
如果 offset 相同,run id 越小,优先级越高
更多 Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
(2)更新配置纪元:
每个主节点会去更新配置纪元(clusterNode.configEpoch),这个值是不断增加的整数。这个值记录了每个节点的版本和整个集群的版本。每当发生重要事情的时候(例如:出现新节点,从节点精选)都会增加全局的配置纪元并且赋给相关的主节点,用来记录这个事件。更新这个值目的是,保证所有主节点对这件“大事”保持一致,大家都统一成一个配置纪元,表示大家都知道这个“大事”了。
(3)发起选举:
更新完配置纪元以后,从节点会向集群发起广播选举的消息(CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST),要求所有收到这条消息,并且具有投票权的主节点进行投票。每个从节点在一个纪元中只能发起一次选举。
(4)选举投票:
如果一个主节点具有投票权,并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示这个主节点支持从节点成为新的主节点。每个参与选举的从节点都会接收 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持。
如果超过(N/2 + 1)数量的 master 节点都投票给了某个从节点,那么选举通过,这个从节点可以切换成 master,如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,更新配置纪元,并进行第二轮选举,直到选出新的主节点为止。
在第(1)步排序领先的从节点通常会获得更多的票,因为它触发选举的时间更早一些,获得票的机会更大
2.3、替换主节点:
当满足投票条件的从节点被选出来以后,会触发替换主节点的操作。删除原主节点负责的槽数据,把这些槽数据添加到自己节点上,并且广播让其他的节点都知道这件事情,新的主节点诞生了。
(1)被选中的从节点执行 SLAVEOF NO ONE 命令,使其成为新的主节点
(2)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己
(3)新的主节点对集群进行广播 PONG 消息,告知其他节点已经成为新的主节点
(4)新的主节点开始接收和处理槽相关的请求
备注:如果集群中某个节点的 master 和 slave 节点都宕机了,那么集群就会进入 fail 状态,因为集群的 slot 映射不完整。如果集群超过半数以上的 master 挂掉,无论是否有 slave,集群都会进入 fail 状态。
六、Redis 集群的搭建:
Redis 集群的搭建可以分为以下几个部分:
1、启动节点:将节点以集群模式启动,读取或者生成集群配置文件,此时节点是独立的。
2、节点握手:节点通过 gossip 协议通信,将独立的节点连成网络,主要使用 meet 命令。
3、槽指派:将 16384 个槽位分配给主节点,以达到分片保存数据库键值对的效果。
七、Redis 集群的运维:
1、数据迁移问题:
Redis 集群可以进行节点的动态扩容缩容,这一过程目前还处于半自动状态,需要人工介入。在扩缩容的时候,需要进行数据迁移。而 Redis 为了保证迁移的一致性,迁移所有操作都是同步操作,执行迁移时,两端的 Redis 均会进入时长不等的阻塞状态,对于小 Key,该时间可以忽略不计,但如果一旦 Key 的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换。
2、带宽消耗问题:
Redis 集群是无中心节点的集群架构,依靠 Gossip 协议协同自动化修复集群的状态,但 goosip 有消息延时和消息冗余的问题,在集群节点数量过多的时候,goosip 协议通信会消耗大量的带宽,主要体现在以下几个方面:
消息发送频率:跟 cluster-node-timeout 密切相关,当节点发现与其他节点的最后通信时间超过 cluster-node-timeout/2 时会直接发送 ping 消息
消息数据量:每个消息主要的数据占用包含:slots 槽数组(2kb)和整个集群 1/10 的状态数据
节点部署的机器规模:机器的带宽上限是固定的,因此相同规模的集群分布的机器越多,每台机器划分的节点越均匀,则整个集群内整体的可用带宽越高
集群带宽消耗主要分为:读写命令消耗+Gossip 消息消耗,因此搭建 Redis 集群需要根据业务数据规模和消息通信成本做出合理规划:
在满足业务需求的情况下尽量避免大集群,同一个系统可以针对不同业务场景拆分使用若干个集群。
适度提供 cluster-node-timeout 降低消息发送频率,但是 cluster-node-timeout 还影响故障转移的速度,因此需要根据自身业务场景兼顾二者平衡
如果条件允许尽量均匀部署在更多机器上,避免集中部署。如果有 60 个节点的集群部署在 3 台机器上每台 20 个节点,这是机器的带宽消耗将非常严重
3、Pub/Sub 广播问题:
集群模式下内部对所有 publish 命令都会向所有节点进行广播,加重带宽负担,所以集群应该避免频繁使用 Pub/sub 功能
4、集群倾斜:
集群倾斜是指不同节点之间数据量和请求量出现明显差异,这种情况将加大负载均衡和开发运维的难度。因此需要理解集群倾斜的原因
(1)数据倾斜:
节点和槽分配不均
不同槽对应键数量差异过大
集合对象包含大量元素
内存相关配置不一致
(2)请求倾斜:
合理设计键,热点大集合对象做拆分或者使用 hmget 代替 hgetall 避免整体读取
5、集群读写分离:
集群模式下读写分离成本比较高,直接扩展主节点数量来提高集群性能是更好的选择。
文章来源于肉眼品世界
评论