Redis Cluster实现原理(一)

Redis sentinel虽然解决了Redis的自动故障转移,可是真正使用的仍是单机。当并发量很高的时候,会遇到内存、并发、存储等的瓶颈,这就须要使用Redis的分布式架构来达到负载均衡。node

Redis分布式方案

Redis Sharding

Redis Sharding能够说是Redis Cluster出来以前,业界广泛使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的key进行散列,经过hash函数,特定的key会映射到特定的Redis节点上。这样,客户端就知道该向哪一个Redis节点操做数据。这是一种客户端分区的实现。客户端分片是把分片的逻辑放在Redis客户端实现,经过Redis客户端预先定义好的路由规则,把对Key的访问转发到对应的Redis实例上进行。 redis

在这里插入图片描述

  • 优势:全部的逻辑都是可控的,不依赖于第三方分布式中间件,配置简单,服务端Redis之间无关联。
  • 缺点:客户端没法动态增删服务节点,客户端须要自行维护分发逻辑,可维护性差。

Codis

Codis是一个分布式的Redis解决方案(前豌豆荚团队开源),对于上层的应用来讲,链接Codis Proxy和链接原生的Redis Server没有明显的区别(不支持的命令列表),上层应用能够像使用单机的Redis同样使用,Codis底层会处理请求的转发,不停机的数据迁移等工做,全部后边的一切事情,对于前面客户端来讲是透明的,能够简单的认为后边链接是一个内存无限大的Redis服务。这是一种基于中间件的代理方案,要实现Codis的高可用须要部署多个Codis实例。
算法

ff

Codis 将全部的 key 默认划分为 1024 个槽位(slot),它首先对客户端传过来的 key 进行 crc32 运算计算哈希值,再将 hash 后的整数值对 1024 这个整数进行取模获得一个余数,这个余数就是对应 key 的槽位。每一个槽位都会惟一映射到后面的多个 Redis 实例之一,Codis 会在内存维护槽位和 Redis 实例的映射关系。多个Codis实例之间槽位关系须要经过Zookeeper来维护,因此这又增长了运维的负担。数据库

  • 优势:实现了上层 Proxy 和底层Redis的高可用,数据分片和自动平衡,提供命令行接口和 RESTful API,提供监控和管理界面,能够动态添加和删除Redis节点。
  • 缺点: 部署架构和配置复杂,增长了运维代价。不支持事务和部分命令,增长了代理层致使性能有所下降。因为是非官方的集群方案,因此永远要比Redis官方更慢,新功能要等好久才能与官方同步。

Redis Cluster

Redis 3正式推出了官方集群技术,解决了多Redis实例协同服务问题。Redis Cluster能够说是服务端Sharding分片技术的体现,即将键值按照必定算法合理分配到各个实例分片上,同时各个实例节点协调沟通,共同对外承担一致服务。但实现原理与客户端sharding分片有所不一样,redis cluster引入了槽(相似Codis,但cluster划分出了16384个槽),将全部的数据对应到槽中,而后每一个Redis节点管理一部分槽。缓存

在这里插入图片描述

  • 优势:无中心节点(全部Redis节点都是对等的节点,同步数据使用的是Gossip协议),数据按照槽存储分布在多个 Redis 实例上,能够平滑的进行节点 扩容/缩容,当节点数量改变时,只须要动态更改节点负责的槽就行,这对于客户端来讲是透明的。不须要依赖中间件,运维成本低。
  • 缺点:严重依赖 Redis-trib 工具,缺少监控管理,Failover节点的检测过慢,Gossip协议传播消息到最终一致性有必定的延迟。

数据分区规则

要将整个数据集划分到多个节点上,让每一个节点负责一部分的数据,就须要使用数据分区规则。经常使用的分区规则是哈希分区和顺序分区。网络

  • 哈希分区的特色是离散度好、数据分布与业务无关但没法顺序访问。
  • 顺序分区的特色是离散度易倾斜、数据分布与业务相关但能够顺序访问(主要用于大数据分析的数据库中)。

Redis Cluster采用的是哈希分区规则,经常使用的哈希分区规则也有几种:固定哈希分区、一致性哈希分区、虚拟槽哈希分区。架构

固定哈希分区

根据特定的字段(Redis中就是使用键)使用哈希函数计算出Hash值,而后根据节点的数量N取模,来决定将数据映射到哪个节点中。这种方式优势就是规则设置和实现都很简单,缺点就是扩容或收缩节点会涉及到不少数据的迁移。
并发

在这里插入图片描述

一致性哈希分区

一致性哈希能够很好的解决稳定性问题,能够将全部的存储节点排列在首尾相接的Hash环上(环的大小为2 ^ 32),key在计算Hash后会 按顺时针方向找到最近的存储节点存放数据。而当新增或减小节点时,仅影响该节点在Hash环上顺时针方向的下一个节点。负载均衡

一致性hash

当集群须要扩容时,只会影响环上顺时针方向的下一个节点(会给该节点减小负载)。以下图:
运维

在这里插入图片描述

新增的node5为node4分担了一部分负载,node1到node5之间的key中的数据将会往node5中迁移(若是是用做缓存的话就直接失效),可是对其它节点没有任何影响。

当集群须要缩减时,减小一个节点,也只会影响顺时针方向的下一个节点(这个节点将承接移除节点的全部负载)。

在这里插入图片描述

移除node1,那么node1以前的全部数据将会往node5迁移(若是是缓存会直接失效),node5将要承接node1以前的全部负载。可是对于集群中的其余节点仍是没有任何影响。

经过上面能够发现,一致性哈希分区规则在集群扩容或缩减时并不能达到负载均衡的目的(除非每次增长一倍或减去一半),因此通常分布式系统都会采用虚拟节点(虚拟槽)对一致性哈希进行改进。

虚拟槽哈希分区

虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希算法(Redis使用的是CRC16算法)把全部数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围通常远远大于节点数,好比 Redis Cluster 槽范围是 0 ~ 16383。 槽是集群内数据管理和迁移的基本单位,每一个节点都负责必定量的槽,先经过hash函数将键映射到槽中,而后将数据存储在该槽所对应的节点。这样就解耦了节点和数据的键之间的关系(键不是直接映射到节点上,而是映射到槽上),简化了节点的扩容和收缩。

虚拟槽哈希

经过虚拟槽哈希分区,能够轻松的实现节点的添加和删除。若是我想添加一个新节点Node4,则只须要将一些哈希槽从节点Node1,Node2,Node3移到新节点Node4。相似地,若是我想从集群中删除节点Node1,则只需将Node1所负责的哈希槽移动到Node2和Node3便可。当节点Node1上的槽所有移走时,就能够将其从群集中彻底删除。

由于将哈希槽从一个节点移动到另外一个节点不须要中止操做,因此添加和删除节点或更改节点持有的哈希槽不须要任何停机时间,这保证了集群的高可用。

Gossip协议

在分布式存储中须要提供维护节点元数据的机制,元数据就是指节点负责哪些数据(在Redis中就是指负责哪些槽)以及节点的状态。经常使用的元数据维护方式为集中式和P2P式。Redis Cluster采用的是P2P的Gossip协议。

这个协议的做用就像其名字表示的意思同样,很是容易理解,它的方式其实在咱们平常生活中也很常见,好比电脑病毒的传播,森林大火,细胞扩散,传染病传播等等。

通讯过程

集群中的每一个节点会单独开通一个TCP通道,用于节点之间彼此的通讯(端口号为节点端口加上10000)。

当有节点更新了状态(新节点加入、节点故障、主从角色变化、槽信息变化等)时,该节点会随机向周围几个节点传播消息,收到消息的节点会重复这个过程,最终集群中全部的节点都会收到该消息,达到集群状态最终一致的目的。

Gossip演示

优点

  • 可扩展性:网络能够容许节点的任意增长和减小,新增长的节点的状态最终会与其余节点一致。

  • 容错:网络中任何节点的宕机和重启都不会影响Gossip消息的传播,Gossip 协议具备自然的分布式系统容错特性。

  • 去中心化:Gossip协议不要求任何中心节点,全部节点均可以是对等的,任何一个节点无需知道整个网络情况,只要网络是连通的,任意一个节点就能够把消息散播到全网。

  • 最终一致性:Gossip协议实现信息指数级(logN)的快速传播,所以在有新信息须要传播时,消息能够快速地发送到全局节点,在有限的时间内可以作到全部节点都拥有最新的数据。

缺陷

  • 消息的延迟:因为Gossip协议中,节点只会随机向少数几个节点发送消息,消息最终是经过多个轮次的散播而到达全网的,所以使用 Gossip 协议会形成不可避免的消息延迟。不适合用在对实时性要求较高的场景下。

  • 消息冗余:Gossip协议规定,节点会按期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤,所以就不可避免的存在消息重复发送给同一节点的状况,形成了消息的冗余,同时也增长了收到消息的节点的处理压力。并且,因为是按期发送,所以,即便收到了消息的节点还会反复收到重复消息,加剧了消息的冗余。

相关文章
相关标签/搜索