Redis实践(九)-初识Redis Cluster

  1. 云栖社区>
  2. 博客>
  3. 正文

Redis实践(九)-初识Redis Cluster

javaedge 2018-08-12 19:16:00 浏览575
展开阅读全文
img_2275e02caec0c31adb85df0cd0fa346c.png

img_815c82bf95615e95be23ca8d419b2345.png

img_32d79fc6acde4a256b74d57ff36f3e7b.png

img_d7d779fe58c03affec72c921621299e5.png

img_22c71dcbd59eeb9e4dc5a12b3a1feaff.png

img_9761c3296c63dcb272c98662dd7800ce.png

img_4ac93eb8181041af1c173ada823dcae2.png

img_aa4ac752733789d1490a2f8d9e84b845.png

img_616addc9e76bb9da6479de7c65041830.png

img_d06ed5562dad675dd1f8627923908072.png

img_be988106febf35adaedb2b9c32c927b5.png

img_57724a1a2ab747843e42bc980abfcfd1.png

img_4d1928b129c49e1dcf9cb08fcbb737f3.png

img_885b77720edff404c8d49b916cc33dc5.png

img_f7e130ce9d9e5e2e017cf339fb084370.png

img_71c22fae7df626e16d85cab36808a3e1.png
数据迁移

img_30a560bdaa2c16cfa5765a4b8d7364f6.png

img_bc71c38aa9e5530d103d2226f8efeef1.png

5 一致性哈希

img_4b4f849278b6fde913265055db6a9037.png
一致性哈希

img_6f78085cab97c9af8435f91c5574630d.png
一致性哈希-扩容

img_519631c91e2b41f03143a8714d1a418b.png

6 虚拟槽哈希分区(Redis Cluster所采用)

img_cde3faf5a70a880f06488ca7d9d14b34.png

img_d9e870710f4a3f3d8d5ef6872c364839.png
虚拟槽分配

7 Redis Cluster基本架构

img_3834a2a6587c98818084135ab57662d2.png
单机架构

img_71e032c94d19c9bf401fcfbd6fccdd38.png
分布式架构

img_16a1d7c8f0f16978303d964520fbea63.png
Redis Cluster 架构

img_f5a9a1d21bb59b1e9c79ef3bd8d911fe.png
节点

img_927d6dfc16672356d3c06015d8aefd0c.png
meet

img_32e9405d9ffff6d3bb6940ae0adececc.png

img_0595752e6d8d6a024bb480b4084ef56a.png
指派槽

img_35b2de68317b30ec93a07cde3cf9e997.png
客户端与指派槽

img_619e29d95038df672e42bf6f10163a44.png
Redis Cluster特性

8 Redis Cluster 安装配置

img_a1f5e6332997f877b686393ce2c45147.png
两种安装

img_7e7393456464caf2c9a6a806a4447af1.png
原生命令安装-理解架构

img_64f5c3ebe1e648dd7ff2908e40a393e8.png
配置开启Redis-1

img_f674e7579cb9b773bff1c98e446d4d35.png
配置开启Redis-2

img_1e2609edf248fd827e2e6845aaabbb8b.png
meet

img_ddb37e12e90d1a90ce72489350ce8e80.png
Cluster节点主要配置

img_49a1544256837e9c7ac667828e864580.png
分配槽

img_cad9522c63c7a2369b7dfa5b14a90444.png
设置主从

Redis 3.0之后,节点之间通过去中心化的方式,提供了完整的shardingreplication(复制机制仍使用原有机制,并且具备感知主备的能力)、failover解决方案,称为Redis Cluster
即:将proxy/sentinel的工作融合到了普通Redis节点里
后面将介绍Redis Cluster这种模式下,水平拆分、故障转移等需求的实现方式。

1 拓扑结构

一个Redis Cluster由多个Redis节点组成。不同的节点组服务的数据无交集,每个节点对应数据sharding的一个分片

节点组内部分为主备2类,对应前面叙述的masterslave。两者数据准实时一致,通过异步化的主备复制机制保证

一个节点组有且仅有一个master,同时有0到多个slave。只有master对外提供写服务,读服务可由master/slave提供


img_6b7148e07b46b315194ef955fb0d7799.jpe

上图中,key-value全集被分成了5份,5个slot(实际上Redis Cluster有 16384 [0-16383] 个slot,每个节点服务一段区间的slot,这里面仅仅举例)。A和B为master节点,对外提供写服务。分别负责1/2/3和4/5的slot。A/A1和B/B1/B2之间通过主备复制的方式同步数据。

上述的5个节点,两两通过Redis Cluster Bus交互,相互交换如下的信息:

数据分片(slot)和节点的对应关系;

集群中每个节点可用状态;

集群结构发生变更时,通过一定的协议对配置信息达成一致。数据分片的迁移、主备切换、单点master的发现和其发生主备关系变更等,都会导致集群结构变化。

publish/subscribe(发布订阅)功能,在Cluster版内部实现所需要交互的信息。

Redis Cluster Bus通过单独的端口进行连接,由于Bus是节点间的内部通信机制,交互的是字节序列化信息。相对Client的字符序列化来说,效率较高。

Redis Cluster是一个去中心化的分布式实现方案,客户端和集群中任一节点连接,然后通过后面的交互流程,逐渐的得到全局的数据分片映射关系。

配置的一致性

对于去中心化的实现,集群的拓扑结构并不保存在单独的配置节点上,后者的引入同样会带来新的一致性问题。那么孤立的节点间,如何对集群的拓扑达成一致,是Redis Cluster配置机制要解决的问题。Redis Cluster通过引入2个自增的Epoch变量,来使得集群配置在各个节点间最终达成一致。

1、配置信息数据结构

Redis Cluster中的每个节点都保存了集群的配置信息,并且存储在clusterState中,结构如下:

img_dbadac4dbc43cb2329fd3b3bd493af52.png
image

上图的各个变量语义如下:

clusterState 记录了从集群中某个节点视角,来看集群配置状态;

currentEpoch 表示整个集群中最大的版本号,集群信息每变更一次,改版本号都会自增。

nodes 是一个列表,包含了本节点所感知的,集群所有节点的信息(clusterNode),也包含自身的信息。

clusterNode 记录了每个节点的信息,其中包含了节点本身的版本 Epoch;自身的信息描述:节点对应的数据分片范围(slot)、为master时的slave列表、为slave时的master等。

每个节点包含一个全局唯一的NodeId。

当集群的数据分片信息发生变更(数据在节点间迁移时),Redis Cluster 仍然保持对外服务。

当集群中某个master出现宕机时,Redis Cluster 会自动发现,并触发故障转移的操作。会将master的某个slave晋升为新的 master。

由此可见,每个节点都保存着Node视角的集群结构。它描述了数据的分片方式,节点主备关系,并通过Epoch 作为版本号实现集群结构信息的一致性,同时也控制着数据迁移和故障转移的过程。

2、信息交互

去中心化的架构不存在统一的配置中心。在Redis Cluster中,这个配置信息交互通过Redis Cluster Bus来完成(独立端口)。Redis Cluster Bus 上交互的信息结构如下:

img_c9c934d958fb43c837e51cbffdb13a5a.png
image

clusterMsg 中的type指明了消息的类型,配置信息的一致性主要依靠PING/PONG。每个节点向其他节点频繁的周期性的发送PING/PONG消息。对于消息体中的Gossip部分,包含了sender/receiver 所感知的其他节点信息,接受者根据这些Gossip 跟新对集群的认识。

对于大规模的集群,如果每次PING/PONG 都携带着所有节点的信息,则网络开销会很大。此时Redis Cluster 在每次PING/PONG,只包含了随机的一部分节点信息。由于交互比较频繁,短时间的几次交互之后,集群的状态也会达成一致。

3、一致性的达成

当Cluster 结构不发生变化时,各个节点通过gossip 协议在几轮交互之后,便可以得知Cluster的结构信息,达到一致性的状态。但是当集群结构发生变化时(故障转移/分片迁移等),优先得知变更的节点通过Epoch变量,将自己的最新信息扩散到Cluster,并最终达到一致。

clusterNode 的Epoch描述的单个节点的信息版本;

clusterState 的currentEpoch 描述的是集群信息的版本,它可以辅助Epoch 的自增生成。因为currentEpoch 是维护在每个节点上的,在集群结构发生变更时,Cluster 在一定的时间窗口控制更新规则,来保证每个节点的currentEpoch都是最新的。

更新规则如下:

1、当某个节点率先知道了变更时,将自身的currentEpoch 自增,并使之成为集群中的最大值。再用自增后的currentEpoch 作为新的Epoch 版本;

2、当某个节点收到了比自己大的currentEpoch时,更新自己的currentEpoch;

3、当收到的Redis Cluster Bus 消息中的某个节点的Epoch > 自身的时,将更新自身的内容;

4、当Redis Cluster Bus 消息中,包含了自己没有的节点时,将其加入到自身的配置中。

上述的规则保证了信息的更新都是单向的,最终朝着Epoch更大的信息收敛。同时Epoch也随着currentEpoch的增加而增加,最终将各节点信息趋于稳定。

sharding

不同节点分组服务于相互无交集的分片(sharding),Redis Cluster 不存在单独的proxy或配置服务器,所以需要将客户端路由到目标的分片。

1、数据分片(slot)

Redis Cluster 将所有的数据划分为16384 [0-16383] 个分片,每个分片负责其中一部分。每一条数据(key-value)根据key值通过数据分布算法(一致性哈希)映射到16384 个slot中的一个。数据分布算法为:

slotId = crc16(key) % 16384
img_45cf913e5d9d3c9b2058033056d3dd23.gif
image.gif

客户端根据slotId 决定将请求路由到哪个Redis 节点。Cluster 不支持跨节点的单命令,如:sinterstore,如果涉及的2个key对应的slot 在不同的Node,则执行失败。通常Redis的key都是带有业务意义的,如:Product:Trade:20180890310921230001、Product:Detail:20180890310921230001。当在集群中存储时,上述同一商品的交易和详情可能会存储在不同的节点上,进而对于这2个key 不能以原子的方式操作。为此,Redis引入了HashTag的概念,使得数据分布算法可以根据key 的某一部分进行计算,让相关的2 条记录落到同一个数据分片。如:

商品交易记录key:Product:Trade:{20180890310921230001}
商品详情记录key:Product:Detail:{20180890310921230001}
img_45cf913e5d9d3c9b2058033056d3dd23.gif
image.gif

Redis 会根据 {} 之间的字符串作为数据分布式算法的输入。

2、客户端的路由

Redis Cluster的客户端相比单机Redis 需要具备路由语义的识别能力,且具备一定的路由缓存能力。当Client 访问的key 不在当前Redis 节点的slots中,Redis 会返回给Client 一个moved命令。并告知其正确的路由信息,如下所示:

img_da6ad1d43eea985c33d0c6937de3dfee.jpe
image

当Client 接收到moved 后,再次请求新的Redis时,此时Cluster 的结构又可能发生了变化。此时有可能再次返回moved 。Client 会根据moved响应,更新其内部的路由缓存信息,以便后续的操作直接找到正确的节点,减少交互次数。

当Cluster 在数据重新分布过程中时,可以通过ask 命令控制客户端的路由,如下所示:

img_3daf3a67688c0a6467a51e791c4b911a.jpe
image

​​​​​​​

上图中,slot 1 需要迁移到新节点上,此时如果客户端已经完成迁移的key,节点将相应ask 告知客户端想目标节点重试。

ask命令和moved 命令的不同在于,moved 会更新Client数据路由,ask 只是重定向新节点,但是后续的相同slot 仍会路由到旧节点。

迁移的过程可能会持续一段时间,这段时间某个slot 的数据,同时可能存在于新旧 2 个节点。由于move 操作会使Client 的路由缓存变更,如果新旧节点对于迁移中的slot 所有key 都回应moved,客户端的路由缓存会频繁变更。因此引入ask 类型消息,将重定向和路由缓存分离。

3、分片的迁移

在一个稳定的Redis Cluster 中,每个slot 对应的节点都是确定的。在某些情况下,节点和分片需要变更:

新的节点作为master加入;

某个节点分组需要下线;

负载不均衡需要调整slot 分布。

此时需要进行分片的迁移,迁移的触发和过程控制由外部系统完成。Redis Cluster 只提供迁移过程中需要的原语,包含下面 2 种:

节点迁移状态设置:迁移前标记源/目标节点。
key迁移的原子化命令:迁移的具体步骤。
img_45cf913e5d9d3c9b2058033056d3dd23.gif
image.gif

下面的Demo会介绍slot 1 从节点A 迁移到B的过程。

img_d771da7cb923b875f0ad26b229195079.jpe
image

1、向节点B发送状态变更命令,将B的对应slot 状态置为importing。

2、向节点A发送状态变更命令,将A对应的slot 状态置为migrating。

3、针对A上的slot 的所有key,分别向A 发送migrate 命令,告知A 将对应的key 迁移到B。

当A节点的状态置为migrating 后,表示对应的slot 正在从A迁出,为保证该slot 数据的一致性。A此时提供的写服务和通常状态下有所区别,对于某个迁移中的slot:

如果Client 访问的key 尚未迁出,则正常的处理该key;

如果key已经迁出或者key不存在,则回复Client ASK 信息让其跳转到B处理;
img_45cf913e5d9d3c9b2058033056d3dd23.gif
image.gif

当节点B 状态变成importing 后,表示对应的slot 正在向B迁入。即使B 能对外提供该slot 的读写服务,但是和通常情况下有所区别:

当Client的访问不是从ask 跳转的,说明Client 还不知道迁移。有可能操作了尚未迁移完成的,处在A上面的key,如果这个key 在A上被修改了,则后续会产生冲突。

所以对于该slot 上所有非ask 跳转的操作,B不会进行操作,而是通过moved 让Client 跳转至A执行。
img_45cf913e5d9d3c9b2058033056d3dd23.gif
image.gif

这样的状态控制,保证了同一个key 在迁移之前总是在源节点执行。迁移后总是在目标节点执行,从而杜绝了双写的冲突。迁移过程中,新增加的key 会在目标节点执行,源节点不会新增key。使得迁移有界限,可以在某个确定的时刻结束。

单个key 的迁移过程可以通过原子化的migrate 命令完成。对于A/B的slave 节点,是通过主备复制,从而达到增删数据。

当所有key 迁移完成后,Client 通过 cluster setslot 命令设置B的分片信息,从而包含了迁入的slot。设置过程中会让Epoch自增,并且是Cluster 中的最新值。然后通过相互感知,传播到Cluster 中的其他节点。

failover

同Sentinel 一样,Redis Cluster 也具备一套完整的故障发现、故障状态一致性保证、主备切换机制。

1、failover的状态变迁

1)故障发现:当某个master 宕机时,宕机时间如何被集群其他节点感知。

2)故障确认:多个节点就某个master 是否宕机如何达成一致。

3)slave选举:集群确认了某个master 宕机后,如何将它的slave 升级成新的master;如果有多个slave,如何选择升级。

4)集群结构变更:成功选举成为master后,如何让整个集群知道,以更新Cluster 结构信息。

2、故障发现

Redis Cluster 节点间通过Redis Cluster Bus 两两周期性的PING/PONG 交互。当某个节点宕机时,其他Node 发出的PING消息没有收到响应,并且超过一定时间(NODE_TIMEOUT)未收到,则认为该节点故障,将其置为PFAIL状态(Possible Fail)。后续通过Gossip 发出的PING/PONG消息中,这个节点的PFAIL 状态会传播到集群的其他节点。

Redis Cluster 的节点两两通过TCP 保持Redis Cluster Bus连接,当对PING 无反馈时,可能是节点故障,也可能是TCP 链接断开。如果是TCP 断开导致的误报,虽然误报消息会因为其他节点的正常连接被忽略,但是也可以通过一定的方式减少误报。Redis Cluster 通过 预重试机制 排除此类误报:当 NODE_TIMEOUT/2 过去了,但是还未收到响应,则重新连接重发PING 消息,如果对端正常,则在很短的时间内就会有响应。

3、故障确认

对于网络分隔的情况,某个节点(B)并没有故障,但是和A 无法连接,但是和C/D 等其他节点可以正常联通。此时只会有A 将 B 标记为PFAIL 状态,其他节点认为B 正常。此时A 和C/D 等其他节点信息不一致,Redis Cluster 通过故障 确认协议 达成一致。

集群中每个节点都是Gossip 的接收者,A 也会接收到来自其他节点的Gossip 消息,被告知B 是否处于PFAIL 状态。当A收到来气其他master 节点对于 B 的PFAIL 达到一定数量后,会将B的PFAIL 状态升级为 FAIL 状态。表示B 已经确认为故障态,后面会发起slave 选举流程。

A节点内部的集群信息中,对于B的状态从PFAIL 到 FAIL 的变迁,如下图所示:

img_b981b50cf944268a8b74265680acdf9d.png
image

4、slave选举

上图中,B是A的master,并且B 已经被集群公认是FAIL 状态了,那么A 发起竞选,期望成为新的master。

如果B 有多个slave (A/E/F)都认知到B 处于FAIL 状态了,A/E/F 可能会同时发起竞选。当B的slave 个数 >= 3 时,很有可能产生多轮竞选失败。为了减少冲突的出现,优先级高的slave 更有可能发起竞选,从而提升成功的可能性。这里的优先级是slave的数据最新的程度,数据越新的(最完整的)优先级越高。

slave 通过向其他master发送FAILVOER_AUTH_REQUEST 消息发起竞选,master 收到后回复FAILOVER_AUTH_ACK 消息告知是否同意。slave 发送FAILOVER_AUTH_REQUEST 前会将currentEpoch 自增,并将最新的Epoch 带入到FAILOVER_AUTH_REQUEST 消息中,如果自己未投过票,则回复同意,否则回复拒绝。

5、结构变更通知

当slave 收到过半的master 同意时,会替代B 成为新的master。此时会以最新的Epoch 通过PONG 消息广播自己成为master,让Cluster 的其他节点尽快的更新拓扑结构。

当B 恢复可用之后,它手续爱你仍然认为自己是master,但逐渐的通过Gossip 协议得知A 已经替代了自己,然后降级为A的slave。

可用性和性能

Redis Cluster 还提供了一些方法可以提升性能和可用性。

1、Redis Cluster的读写分离

对于读写分离的场景,应用对于某些读请求允许舍弃一定的数据一致性,以换取更高的吞吐量。此时希望将读请求交给slave处理,以分担master的压力。

通过分片映射关系,某个slot 一定对应着一个master节点。Client 通过moved 命令,也只会路由到各个master中。即使Client 将请求直接发送到slave上,也会回复moved 到master去处理。

为此,Redis Cluster 引入了readonly 命令。Client 向slave发送该命令后,不再moved 到master处理,而是自己处理,这成为slave的readonly 模式。通过readwrite命令,可以将slave的readonly模式重置。

2、master单点保护

假如Cluster 的初始状态如下所示:

img_346501afec249a2d29f0ea4c9c7a588b.jpe
image

上图中A、B两个master 分别有自己的slave,假设A1 发生宕机,结构变为如下所示:

img_9a56dc72ca1d338761be1ca8372dc10a.jpe
image

此时A 成为了单点,一旦A 再次宕机,将造成不可用。此时Redis Cluster 会把B 的某个slave (如 B1 )进行副本迁移,变成A的slave。如下所示:

img_5db4496744de8c4e0088d73f4e6bd8db.jpe
image

这样集群中每个master 至少有一个slave,使得Cluster 具有高可用。集群中只需要保持 2*master+1 个节点,就可以保持任一节点宕机时,故障转移后继续高可用。


img_525f8d1c1ac79edbc8260e1f9de2d513.png

网友评论

登录后评论
0/500
评论
javaedge
+ 关注