自律是最强者的本能,当你把自律作为习惯,那你已经成为强者。
在讲Redis集群架构之前,我们先简单讲下Redis单实例的架构,从最开始的一主N从,到读写分离,再到Sentinel哨兵机制,单实例的Redis缓存足以应对大多数的使用场景,也能实现主从故障迁移。
但是,在某些场景下,单实例存Redis缓存会存在的几个问题:
(内存容量的限制)Redis的最大缺点和局限性就在于内存存储数据,这样子对容量而言会有相当大的限制。
针对以上的问题,Redis集群提供了较为完善的方案,解决了存储能力受到单机限制,写操作无法负载均衡的问题。
【Redis集群】是一种服务器Sharding(分片)技术,3.0版本开始正式提供。
Redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台Redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了Cluster集群模式,实现了Redis的分布式存储,也就是说每台 Redis 节点上存储不同的内容。
(分片存储)Redis3.0加入了 Redis 的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的master节点上面,从而解决了海量数据的存储问题。
(指令转换)Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一Redis实例一样,不需要任何代理中间件,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的Redis节点。
如上图所示,Redis集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有master节点具有处理请求的能力,slave节点主要是用于节点的高可用)
前面讲到,Redis集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的Redis服务器上。常见的分区算法有hash算法、一致性hash算法,关于这些算法这里就不多介绍。
如果要实现 Redis 数据的分片,我们有三种方案。
(客户端负载)第一种是在客户端实现相关的逻辑,例如用取模或者一致性哈希对key进行分片,查询和修改都先判断key的路由。
如果是希望数据分布相对均匀的话,可以考虑哈希后取模。
将key使用hash算法计算之后:hash(key)%N,根据余数,决定映射到那一个节点。
把所有的哈希值空间组织成一个虚拟的圆环(哈希环),整个空间按顺时针方向组织。因为是环形空间,0 和 2^32-1 是重叠的(总共2^32个)。
最后顺时针找到第一个大于等于该哈希值的第一个Node,就是数据存储的节点。
Redis 集群既没有用哈希取模,也没有用一致性哈希,而是用Hash槽来实现的。Redis集群创建了 16384 个槽(slot),每个节点负责一定区间的slot。
Redis集群中有16384(2^14)个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分区间的哈希槽。
集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5460 号哈希槽
节点 B 包含 5461 到 10922 号哈希槽
这种结构很容易添加或者删除节点。
(1)如果我想新添加个节点 D , 我需要从节点 A, B, C 中得部分槽到 D 上。
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
redis> cluster keyslot yan
注意:key 与 slot 的关系是永远不会变的,会变的只有 slot 和 Redis 节点的关系
在key里面加入{hash tag}即可。Redis在计算槽编号的时候只会获取{}之间的字符串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可以被计算出相同的槽
客户端连接到哪一台服务器?访问的数据不在当前节点上,怎么办?
比如在 7291 端口的 Redis 的 redis-cli 客户端操作:
127.0.0.1:7291> set qs 1 (error) MOVED 13724 127.0.0.1:7293
服务端返回 MOVED,也就是根据 key 计算出来的 slot 不归 7191 端口管理,而是 归 7293 端口管理,服务端返回 MOVED 告诉客户端去 7293 端口操作。
因为 key 和 slot 的关系是永远不会变的,当新增了节点的时候,需要把原有的 slot 分配给新的节点负责,并且把相关的数据迁移过来。
redis-cli --cluster reshard 127.0.0.1:7291
CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
输入需要分配的哈希槽的数量(比如 500),和哈希槽的来源节点(可以输入 all 或 者 id)
为了保证高可用,redis-cluster集群引入了主从复制模型,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点 。
默认情况下,redis集群的读和写都是到master上去执行的,不支持slave节点读和写,跟Redis主从复制下读写分离不一样,因为redis集群的核心的理念,主要是使用slave做数据的热备,以及master故障时的主备切换,实现高可用的。
redis集群内部的节点是相互通信的(PING-PONG机制),每个节点都是一个redis实例;内部使用二进制协议优化传输速度和带宽。
为了实现集群的高可用,即判断节点是否健康(能否正常使用),redis-cluster有这么一个投票容错机制:如果集群中超过半数的节点投票认为某个节点挂了,那么这个节点就挂了(fail)。这是判断节点是否挂了的方法
Redis集群是没有统一的入口的,客户端与 Redis 节点直连,不需要中间代理层,也不需要连接集群所有节点,即连接集群中任何一个可用节点即可。
客户端(client)连接集群的时候连接集群中的任意节点(node)即可。
Redis集群至少需要3个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以2个节点无法构成集群。
无中心架构。
数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布。
可扩展性,可线性扩展到1000个节点(官方推荐不超过 1000 个)节点可动态添加或删除。
高可用性,部分节点不可用时,集群仍可用。通过增加Slave做 standby 数据副本,能够实现故障自动failover,节点之间通过gossip协议交换状态信息,用投票机制完成 Slave到Master的角色提升。
Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。
节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover是没有必要的。
数据通过异步复制,不保证数据的强一致性