Redis Cluster实现原理

本文探讨了Redis Cluster的实现原理,重点关注其分片策略。通过GET X操作的MOVED响应,揭示了Redis如何在集群中进行数据迁移和节点间的通信,确保高可用性和高性能的数据访问。
摘要由CSDN通过智能技术生成
一、Redis Cluster主要特性和设计
    集群目标
    1)高性能和线性扩展,最大可以支撑到1000个节点;Cluster架构中无Proxy层,Master与slave之间使用异步replication,且不存在操作的merge。(即操作不能跨多个nodes,不存在merge层)
    2)一定程度上保证writes的安全性,需要客户端容忍一定程度的数据丢失:集群将会尽可能(best-effort)保存客户端write操作的数据;通常在failover期间,会有短暂时间内的数据丢失(因为异步replication引起);当客户端与少数派的节点处于网络分区时(network partition),丢失数据的可能性会更高。(因为节点有效性检测、failover需要更长的时间)
    3)可用性:只要集群中大多数master可达、且失效的master至少有一个slave可达时,集群都可以继续提供服务;同时“replicas migration”可以将那些拥有多个slaves的master的某个slave,迁移到没有slave的master下,即将slaves的分布在整个集群相对平衡,尽力确保每个master都有一定数量的slave备份。
 
    ( Redis Cluster集群有多个shard组成,每个shard可以有一个master和多个slaves构成,数据根据hash slots配额分布在多个shard节点上,节点之间建立双向TCP链接用于有效性检测、Failover等,Client直接与shard节点进行通讯;Cluster集群没有Proxy层,也没有中央式的Master用于协调集群状态或者state存储;集群暂不提供动态reblance策略
    备注:下文中提到的query、查询等语义,泛指redis的读写操作。
 
     Mutli-key操作
    Redis单实例支持的命令,Cluster也都支持,但是对于“multi-key”操作(即一次RPC调用中需要进行多个key的操作)比如Set类型的交集、并集等,则要求这些key必须属于同一个node。Cluster不能进行跨Nodes操作,也没有nodes提供merge层代理。
    Cluster中实现了一个称为“hash tags”的概念,每个key都可以包含一个自定义的“tags”,那么在存储时将根据tags计算此key应该分布在哪个nodes上(而不是使用key计算,但是存储层面仍然是key);此特性,可以强制某些keys被保存在同一个节点上,以便于进行“multikey”操作,比如“foo”和“{foo}.student”将会被保存在同一个node上。不过在人工对slots进行resharding期间,multikey操作可能不可用。
    我们在Redis单例中,偶尔会用到“SELECT”指令,即可以将key保存在特定的database中(默认database索引号为0);但是 在Cluster环境下,将不支持SELECT命令,所有的key都将保存在默认的database中
 
     客户端与Server角色
    集群中nodes负责存储数据,保持集群的状态,包括keys与nodes的对应关系(内部其实为slots与nodes对应关系)。nodes也能够自动发现其他的nodes,检测失效的节点,当某个master失效时还应该能将合适的slave提升为master。   
    为了达成这些行为,集群中的每个节点都通过TCP与其他所有nodes建立连接,它们之间的通信协议和方式称为“Redis Cluster Bus”。Nodes之间使用gossip协议(参见下文备注)向其他nodes传播集群信息,以达到自动发现的特性,通过发送ping来确认其他nodes工作正常,也会在合适的时机发送集群的信息。当然在Failover时(包括人为failover)也会使用Bus来传播消息。
     (gossip:最终一致性,分布式服务数据同步算法,node首选需要知道(可以读取配置)集群中至少一个seed node,此node向seed发送ping请求,此时seed节点pong返回自己已知的所有nodes列表,然后node解析nodes列表并与它们都建立tcp连接,同时也会向每个nodes发送ping,并从它们的pong结果中merge出全局nodes列表,并逐步与所有的nodes建立连接.......数据传输的方式也是类似,网络拓扑结构为full mesh)
    因为Node并不提供Proxy机制,当Client将请求发给错误的nodes时(此node上不存在此key所属的slot),node将会反馈“MOVED”或者“ASK”错误信息,以便Client重新定向到合适的node。理论上,Client可以将请求发送给任意一个nodes,然后根据在根据错误信息转发给合适的node,客户端可以不用保存集群的状态信息,当然这种情况下性能比较低效,因为Client可能需要2次TCP调用才能获取key的结果,通常客户端会缓存集群中nodes与slots的映射关系,并在遇到“Redirected”错误反馈时,才会更新本地的缓存。
 
     安全写入(write safety)
    在Master-slaves之间使用异步replication机制,在failover之后,新的Master将会最终替代其他的replicas(即slave)。在出现网络分区时(network partition),总会有一个窗口期(node timeout)可能会导致数据丢失;不过,Client与多数派的Master、少数派Master处于一个分区(网络分区,因为网络阻断问题,导致Clients与Nodes被隔离成2部分)时,这两种情况下影响并不相同。
    1)write提交到master,master执行完毕后向Client反馈“OK”,不过此时可能数据还没有传播给slaves(异步replication);如果此时master不可达的时间超过阀值(node timeout,参见配置参数),那么将触发slave被选举为新的Master(即Failover),这意味着那些没有replication到slaves的writes将永远丢失了!
    2)还有一种情况导致数据丢失:
        A)因为网络分区,此时master不可达,且Master与Client处于一个分区,且是少数派分区。
        B)Failover机制,将其中一个slave提升为新Master。
        C)此后网络分区消除,旧的Master再次可达,此时它将被切换成slave。
        D)那么在网络分区期间,处于少数派分区的Client仍然将write提交到旧的Master,因为它们觉得Master仍然有效;当旧的Master再次加入集群,切换成slave之后,这些数据将永远丢失。


 
    在第二种情况下,如果Master无法与其他大多数Masters通讯的时间超过阀值后,此Master也将不再接收Writes,自动切换为readonly状态。当网络分区消除后,仍然会有一小段时间,客户端的write请求被拒绝,因为此时旧的Master需要更新本地的集群状态、与其他节点建立连接、角色切换为slave等等,同时Client端的路由信息也需要更新。
    只有当此master被大多数其他master不可达的时间达到阀值时,才会触发Failover,这个时间称为NODE_TIMEOUT,可以通过配置设定。所以当网络分区在此时间被消除的话,writes不会有任何丢失。反之,如果网络分区持续时间超过此值,处于“小分区”(minority)端的Master将会切换为readonly状态,拒绝客户端继续提交writes请求,那么“大分区”端将会进行failover,这意味着NODE_TIMEOUT期间发生在“小分区”端的writes操作将丢失(因为新的Master上没有同步到那些数据)。 
 
     可用性
    处于“小分区”的集群节点是不可用的;“大分区”端必须持有大多数Masters,同时每个不可达的Master至少有一个slave也在“大分区”端,当NODE_TIMEOUT时,触发failover,此后集群才是可用的。Redis Cluster在小部分nodes失效后仍然可以恢复有效性,如果application希望大面积节点失效仍然有效,那么Cluster不适合这种情况。
    比如集群有N个Master,且每个Master都有一个slave,那么集群的有效性只能容忍一个节点(master)被分区隔离(即一个master处于小分区端,其他处于大分区端),当第二个节点被分区隔离之前仍保持可用性的概率为1 - (1/(N * 2 - 1))(解释:当第一个节点失效后,剩余N * 2 -1个节点,此时没有slave的Master失效的概率为1/(N * 2 -1))。比如有5个Master,每个Master有一个slave,当2个nodes被隔离出去(或者失效)后,集群可用性的概率只有1/(5 * 2 - 1) = 11.11%,因此集群不再可用。
    幸好Redis Cluster提供了“replicas migration”机制,在实际应用方面,可以有效的提高集群的可用性,当每次failover发生后,集群都会重新配置、平衡slaves的分布,以更好的抵御下一次失效情况的发生。(具体参见下文)
 
     性能
    Redis Cluster并没有提供Proxy层,而是告知客户端将key的请求转发给合适的nodes。Client保存集群中nodes与keys的映射关系(slots),并保持此数据的更新,所以通常Client总能够将请求直接发送到正确的nodes上。因为采用异步replication,所以master不会等待slaves也保存成功后才向客户端反馈结果,除非显式的指定了WAIT指令。multi-key指令仅限于单个节点内,除了resharding操作外,节点的数据不会在节点间迁移。每个操作只会在特定的一个节点上执行,所以集群的性能为master节点的线性扩展。同时,Clients与每个nodes保持链接,所以请求的延迟等同于单个节点,即请求的延迟并不会因为Cluster的规模增大而受到影响。高性能和扩展性,同时保持合理的数据安全性,是Redis Cluster的设计目标。
 
    Redis Cluster没有Proxy层,Client请求的数据也无法在nodes间merge;因为Redis核心就是K-V数据存储,没有scan类型(sort,limit,group by)的操作,因此merge操作并不被Redis Cluster所接受,而且这种特性会极大增加了Cluster的设计复杂度。(类比于mongodb)
 
二、Cluster主要组件
     keys分布模型
    集群将key分成16384个slots(hash 槽),slot是数据映射的单位,言外之意,Redis Cluster最多支持16384个nodes(每个nodes持有一个slot)。集群中的每个master持有16384个slots中的一部分,处于“stable”状态时,集群中没有任何slots在节点间迁移,即任意一个hash slot只会被单个node所服务(master,当然可以有多个slave用于replicas,slave也可以用来扩展read请求)。keys与slot的映射关系,是按照如下算法计算的:HASH_SLOT = CRC16(key) mod 16384。其中CRC16是一种冗余码校验和,可以将字符串转换成16位的数字。
 
     hash tags
    在计算hash slots时有一个意外的情况,用于支持“hash tags”;hash tags用于确保多个keys能够被分配在同一个hash slot中,用于支持multi-key操作。hash tags的实现比较简单,key中“{}”之间的字符串就是当前key的hash tags,如果存在多个“{}”,首个符合规则的字符串作为hash tags,如果“{}”存在多级嵌套,那么最内层首个完整的字符串作为hash tags,比如“{foo}.student”,那么“foo”是hash tags。如果key中存在合法的hash tags,那么在计算hash slots时,将使用hash tags,而不再使用原始的key。即“foo”与“{foo}.student”将得到相同的slot值,不过“{foo}.student”仍作为key来保存数据,即redis中数据的key仍为“{foo}.student”。
 
     集群节点的属性
    集群中每个节点都有唯一的名字,称之为node ID,一个160位随机数字的16进制表示,在每个节点首次启动时创建
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值