资讯专栏INFORMATION COLUMN

Redis Cluster

187J3X1 / 1233人阅读

摘要:通过作为版本号来实现集群配置的一致性。配置信息的一致性主要依靠和,两者除了不同,其余字段语义均相同,消息体为数据。每一个节点向其他节点较为频繁的周期性发送消息和接受响应。同一样,具备完整的节点故障发现故障状态一致性保证主备切换机制。

Redis3.0以后,节点之间通过去中心化的方式提供了完整的sharding(数据分片)、replication(复制机制、Cluster具备感知准备的能力)、failover解决方案

拓扑结构

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

节点组内分为主备两类节点,两者数据准实时一致,通过异步化的主备复制机制

master节点对用户提供读写服务,slave节点对用户提供读服务。

Redis Cluster总共有16384个slot,每一个节点负责一部分slot。

Redis Cluster中所有的几点之间两两通过Redis Cluster Bus交互,主要交互以下关键信息:

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

集群中每个节点可用状态

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

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

Redis Cluster Bus通过多带带的端口进行连接,bus是节点间的内部通信机制,交互的是字节序列化信息,而不是client到Redis服务器的字符序列化以提升交互效率。

Redis Cluster是去中心化的分布式实现方案,客户端可以和集群中的任一节点连接。

配置一致性

去中心化意味着集群的拓扑结构并不保存在多带带的配置节点上,Redis Cluster通过引入两个自增的epoch变量来使得集群配置在各个节点间达成最终一致。

配置信息的数据结构

Redis Cluster中的每一个节点都保存了集群的配置信息,这些信息存储在clusterState中。

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

currentEpoch表示整个集群中的最大版本号,集群信息每变更一次,该版本号都会自增以保证每个信息的版本号唯一

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

clusterNode记录了每个节点的信息,比较关键的信息包括该信息的版本epoch,该版本信息的描述:该节点对应的数据分片(slot),当该节点为master节点时对应的slave节点列表、当该节点为slave时对应的master节点

每个clusterNode还包含了一个全局唯一的nodeId

当集群的数据分片信息发生变更时,Redis Cluster仍然保持对外服务,在迁移过程中,通过分片迁移相关状态的一组变量来管控迁移过程

当集群中的某个master出现宕机时,Redis Cluster会自动发现并触发故障转移的操作,将宕机master的某个slave升级为master,这个过程同样需要一组failover相关状态的变量来管控故障转移。

Redis Cluster通过epoch作为版本号来实现集群配置的一致性。

信息交互

去中心化的架构不存在统一的配置中心,各个节点对集群状态的认知来自于节点间的信息交互。在Redis Cluster中,该信息的交互通过Redis Cluster Bus来完成。

clusterMsg的type字段指明了消息的类型。配置信息的一致性主要依靠PING和PONG,两者除了type不同,其余字段语义均相同,消息体为Gossip数据。

每一个节点向其他节点较为频繁的周期性发送PING消息和接受PONG响应。在这些下拍戏的Gossip部分,包含了发送者节点(或者响应者节点)所知的集群其他节点信息,接收节点可以根据这些Gossip信息更新自己对于集群的认知。

规模较大的集群可能存在上千个节点,但是这些节点在正常情况下都是稳定的,因此每次都发送全量数据并不必要,而且还会造成网络负担。

作为优化,Redis Cluster在每次的PING和PONG包中,只包含全集群部分节点信息,节点随机选取,以此控制网络流量。由于交互频繁,短时间的几次交互之后,集群状态就会以Gossip协议的方式被扩散到了集群中的所有节点。

一致性达成

集群结构稳定不发生变化时,各个节点通过Gossip协议在几轮交互之后便可得知全集群的信息并且达到一致的状态。

但是,当发生故障转移、分片迁移等情况将会造成集群结构变更,变更的信息需要各个节点之间自行协调,优先得知变更信息的节点利用epoch变量将自己的最新信息扩散到整个集群,达到最终一致。

配置信息clusterNode的epoch属性描述的粒度是单个节点

配置信息clusterState的currentEpoch属性的粒度是整个集群,它的存在用来辅助epoch自增的生成。由于currentEpoch信息也是维护在各个几点自身的,Redis Cluster结构在发生变更时,通过一定时间窗口控制和更新规则保证每个节点看到的currentEpoch都是最新的。

集群信息的更新规则:

当某个节点率先知道了信息变更时,这个节点将currentEpoch自增使之成为集群中的最大值,再用自增后的currentEpoch作为新的epoch版本

当某个节点收到了比自己大的currentEpoch时,更新自己的currentEpoch值使之保持最新

当收到的Redis Cluster Bus消息中某个节点信息的epoch值大于接收者自己内部的配置信息存储的值时,意味着自己的信息太旧,此时接收者直接将自己的映射信息更新为消息的内容

当收到的Redis Cluster Bus消息中某个节点信息未包含在接收节点的内部配置信息中时,意味着接受者尚未意识到该节点的存在,此时接收者直接将消息的信息添加到自己的内部配置信息中。

sharding

不同的节点组服务于相互无交互的数据子集(sharding,分片)。

数据分片(slot)

Redis Cluster将所有的数据划分为16384个分片(slot),每个分片负责其中一部分。每一条数据根据key值通过数据分布算法映射到16384个slot中的一个。

数据分布算法:slotId=crc(key)%16384

客户端根据slotId决定将请求路由到哪个Redis节点。Cluster不支持跨节点的单命令

为此,Redis引入HashTag的概念,使得数据分布算法可以根据key的某一部分进行计算,让相关的两条记录落到同一个数据分片,例如:

某条商品交易记录的key值为:product_trade_{prod123}

这个商品的详情记录的key值为:product_detail_{prod123}

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

客户端路由

Redis Cluster的客户端需要具备一定的路由能力。当一个Client访问的key不在对应Redis节点的slot中,Redis返回给Client一个moved命令,告知其正确的路由信息

从Client收到moved响应,到再次向moved响应中指向的节点发送请求期间,Redis Cluster的数据分布可能又发生了变更,此时,指向的节点会继续响应moved。Client根据moved响应更新其内部的路由缓存信息,以便下一次请求时直接路由到正确的节点,降低交互次数。

当Cluster处在数据重分布(目前由人工触发)过程中时,可以通过ask命令控制客户端路由。

ask命令和moved命令的不同语义在于,后者会更新路由缓存,前者只是本条操作重定向到新节点,后续的相同slot操作仍路由到旧节点。ask类型将重定向和路由缓存更新分离,避免客户端的路由缓存信息频繁更新。

分片迁移

在稳定的Redis Cluster下,每一个slot对应的节点是确定的。但是在某些情况下,节点和分片的对应关系要发生变更:

新的节点作为master加入

某个节点分组需要下线

负载不均需要调整slot分布

此时需要进行分片的迁移。分片迁移的触发和过程由外部系统完成,Redis Cluster只提供迁移过程中需要的原语供外部系统调用。这些原语主要有两种:

节点迁移状态设置:迁移前标记源/目标节点

key迁移的原子化命令:迁移的具体步骤

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

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

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

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

如果客户端访问的key尚未迁移出,则正常地处理key

如果key已经被迁移出或者根本不存在该key,则回复客户端ASK信息让其跳转到B执行

当节点B的状态被设置为了IMPORTING之后,表示对应的slot正在向B迁入中,即使B仍能对外提供该slot的读写服务,但行为和通常状态下也有所区别:

当来自客户端的正常访问不是从ASK跳转而来时,说明客户端尚不知道迁移正在进行,很有可能操作了一个目前尚未迁移完成的正处在A上的key,如果此时key已经在A上被修改了,那么B和A的修改值将在未来发生冲突。

对于该slot上的所有非ASK跳转而来的操作,B不会进行处理,而是通过MOVED命令让客户端跳转至A执行

这样的状态控制可以保证同一个key在迁移之前总是在源节点执行,迁移后总是在目标节点执行,杜绝了两边同时写导致值冲突的可能性。且迁移过程中新增的key总是在目标节点执行,源节点不会再有新增的key,使得迁移过程时间有界。

Redis单机对于命令的处理是单线程的,同一个key在MIGRATE的过程中不会处理对该key的其他操作,从而保证了迁移的原子性。

当slot的所有key从A迁移至B上之后,客户端通过CLUSTER SETSLOT命令设置B的分片信息,使之包含迁移的slot。设置的过程中会自增一个epoch,它大于当前集群中的所有epoch值,这个新的配置信息会传播到集群中的其他每一个节点,完成分片节点映射关系的更新。

failover

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

failover状态变迁

failover的过程如下:

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

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

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

集群结构变更:选举成功的slave升级成新的master后如何让全集群的其他节点知道以更新他们的集群结构信息

故障发现

Redis Cluster节点间通过Redis Cluster Bus两两周期性地进行PING/PONG交互,当某个节点宕机时,其他发向它的PING消息将无法及时响应,当PONG的响应超过一定时间(NODE_TIMEOUT)未收到,则发送者认为接受节点故障,将其置为PFAIL状态,后续通过Gossip发出的PING/PONG消息中,这个节点的PFAIL状态将会被转播到集群的其他节点。

Redis Cluster的节点间通过TCP保持Redis Cluster Bus连接,当对端无PONG回复时,除了节点故障外,还有可能是TCP连接断开。对于TCP连接断开导致的响应超时,将会产生节点状态误报。因此Redis Cluster通过预重试机制排除此类误报:当NODE_TIMEOUT/2过去了却还未收到PING对应的PONG消息,则重建连接重发PING消息,如果对端正常,PONG会在很短时间内抵达。

故障确认

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

A会受到来自其他节点的Gossip消息,被告知节点B是否处于PFAIL状态,当A受到的来自其他master节点的B的PFAIL达到一定数量后,会将B的PFAIL升级为FAIL状态,表示B已确认为故障,后续将会发起slave选举流程

slave选举

上例中,如果B是A的master,且B已经被集群公认是FAIL状态,那么A将发起竞选,期望替代B成为新的master。

如果B有多个slave A/E/F都意识到B处于FAIL状态了,A/E/F可能会同时发起竞选,当B的slave数量>=3个时,很有可能因为票数均匀无法选出胜者,延长B上的slot不可用时间。为此,slave间会在选举前协商优先级,优先级高的slave更有可能早地发起选举,优先级较低的slave发起选举的时间越靠后,避免和高优先级的slave竞争,提升一轮完成选举的可能性

优先级最重要的决定因素是slave最后一次同步master信息的时间,越新标识这个slave的数据越新,竞选优先级越高

slave通过向其他master节点发送FAILOVER_AUTH_REQUEST消息发起竞选,master收到之后回复FAILOVER_AUTH_ACK消息告知自己是否同意改slave成为新的master。slave发送FAILOVER_AUTH_REQUEST前会将currentEpoch自增并将最新的epoch带入到AILOVER_AUTH_REQUEST消息中,master收到FAILOVER_AUTH_REQUEST消息后,如果发现对于本轮(本epoch)自己尚未投过票,则回复同意,否则回复拒绝。

集群结构变更通知

当slave收到超过半数的master的同意回复时,该slave顺利的替代B成为新master,此时它会以最新的epoch通过PONG消息广播自己成为master的信息,让集群中的其他节点更快地更新拓扑信息。

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

主备复制

Redis采用主备复制的方式保持一致性,即所有节点中,有一个节点为master,对外提供写入服务,所有的数据变更由外界对master的写入触发,之后Redis内部异步地将数据从主节点复制到其他节点上。

主备复制流程

Redis包含master和slave节点:master节点对外提供读写服务;slave节点作为master的数据备份,拥有master的全量数据,对外不提供写服务。主备复制由slave主动触发

slave向master发起SYNC命令。这一步在slave启动后触发,master被动地将新进slave节点加入自己的主备复制集群

master收到SYNC后,开启BGSAVE操作。BGSAVE是Redis的一种全量模式的持久化机制

BGSAVE完成后,master会将快照信息发送给slave

发送期间,master收到的来自客户端的新的写命令,除了正常响应外,都再存入一份到backlog队列

快照信息发送完成后,master继续发送backlog队列信息

backlog发送完成后,后续的写操作同时发送给slave,保持实时地异步复制

slave侧的处理逻辑:

发送完SYNC后,继续对外提供服务

开始接收master的快照信息,此时,将slave现有数据清空,并将master快照写入自身内存

接收backlog内容并执行它,即回放,期间对外提供读请求

继续接收后续来自master的命令副本并继续回放,以保证数据和master一致

如果有多个slave节点并发发送SYNC命令给master,只要第二个slave的SYNC命令发生在master完成BGSAVE之前,第二个slave将受到和第一个slave相同的快照和后续的backlog;否则,第二个slave的SYNC将触发master的第二次BGSAVE

断点续传

slave通过SYNC命令和master进行数据同步时,master都会dump全量数据。假设master和slave断开很短的时间,数据只有很少的差异,重连后也会发送这些全量数据导致大量的无效开销。最好的方式就是,master-slave只同步断开期间的少量数据

Redis的PSYNC可用于替代SYNC,做到master-slave基于断点续传的主备同步协议。master-slave两端通过维护一个offset记录当前已经同步过的命令,slave断开期间,master的客户端命令会保持在缓存中,在slave命令重连后,告知master断开时的最新offset,master则将缓存中大于offset的数据发送给slave,而断开前已经同步过的数据,则不再重新同步,这样减少了数据传输开销

可用性和性能 Redis Cluster读写分离

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

默认情况下,数据分片映射关系中,某个slot对应的节点一定是一个master节点,客户端通过MOVED消息得知的集群拓扑结构也只会将请求路由到各个master中,即便客户将读请求直接发送到slave上,后者也会回复MOVED到master的响应。

Redis Cluster引入了READONLY命令。客户端向slave发送该命令后,slave对于读操作,将不再MOVED回master而不是直接处理,这被称为slave的READONLY模式。通过READWRITE命令,可将slave的READONLY模式重置。

master单点保护

集群只需要保持2*master+1个节点,就可以在任一节点宕机后仍然自动地维持,称为master的单点保护。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37041.html

相关文章

  • docker-compose启动redis多机集群的实现

    第一步环境配置:第二步部署:redis-node-1ymlversion: '2.2' services: redis-node-1: image: bitnami/redis-cluster:7.0 restart: always container_name: redis-node-1 network_mode: host volumes: - ./redis-cluster_...

    3119555200 评论0 收藏0
  • ubuntu/centos 搭建 搭建 redis 集群

    摘要:环境配置机器下载独立文件位置安装关键点工具实现集群复制到目录下。创建集群命令发现环境需要下安装方案下安装方案如果不好用,可以切换到淘宝源然后再次执行即可。说明集群正常工作。互联网时代,时刻要保持学习,携手千锋。 1 环境配置: ubuntu16.04 redis3.0.62 机器: 192.168.50.156 192.168.50.154 192.168.50.1553 下载red...

    jcc 评论0 收藏0
  • ubuntu/centos 搭建 搭建 redis 集群

    摘要:环境配置机器下载独立文件位置安装关键点工具实现集群复制到目录下。创建集群命令发现环境需要下安装方案下安装方案如果不好用,可以切换到淘宝源然后再次执行即可。说明集群正常工作。互联网时代,时刻要保持学习,携手千锋。 1 环境配置: ubuntu16.04 redis3.0.62 机器: 192.168.50.156 192.168.50.154 192.168.50.1553 下载red...

    bbbbbb 评论0 收藏0
  • ubuntu/centos 搭建 搭建 redis 集群

    摘要:环境配置机器下载独立文件位置安装关键点工具实现集群复制到目录下。创建集群命令发现环境需要下安装方案下安装方案如果不好用,可以切换到淘宝源然后再次执行即可。说明集群正常工作。互联网时代,时刻要保持学习,携手千锋。 1 环境配置: ubuntu16.04 redis3.0.62 机器: 192.168.50.156 192.168.50.154 192.168.50.1553 下载red...

    oysun 评论0 收藏0
  • ubuntu/centos 搭建 搭建 redis 集群

    摘要:环境配置机器下载独立文件位置安装关键点工具实现集群复制到目录下。创建集群命令发现环境需要下安装方案下安装方案如果不好用,可以切换到淘宝源然后再次执行即可。说明集群正常工作。互联网时代,时刻要保持学习,携手千锋。 1 环境配置: ubuntu16.04 redis3.0.62 机器: 192.168.50.156 192.168.50.154 192.168.50.1553 下载red...

    stormgens 评论0 收藏0
  • 优雅搭建redis-cluster

    摘要:前言记得年方二八时候,数数还是数得过来的,如今年逾二十八,数都数不清了,前几天在狗东哪里整了算法导论概率导论两本教科书看看学学,也好在研究机器学习的时候有解惑的根本。 前言 记得年方二八时候,数数还是数得过来的,如今年逾二十八,数都数不清了,前几天在狗东哪里整了《算法导论》、《概率导论》两本教科书看看学学,也好在研究机器学习的时候有解惑的根本。redis-cluster正好成功的引起了...

    Tychio 评论0 收藏0

发表评论

0条评论

187J3X1

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<