谈谈Redis的集群
1. 前言
在谈谈redis的哨兵机制这篇文中,我们知道Redis是使用哨兵机制来监控各个节点之间的状态。但是,在分布式的环境下,哨兵机制就不能很好地监控各个节点之前的状态。为了解决这个问题,Redis在3.0的版本推出了Redis Cluster,即redis集群。本篇就谈谈redis集群,主要介绍常用的几种Redis集群方案。
2. Redis集群方案
Redis Cluster集群模式通常具有高可用、可扩展性、分布式、容错等特性。Redis分布式方案一般有三种:客户端分区方案、代理分区方案、查询路由方案。
2.1 客户端分区方案
这种方案顾名思义,客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取数据。其主要思想是采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。通过一个简单的示例图描述下这种方案,如下所示:
客户端分区方案的代表为Redis-Sharding,Redis-Sharding是Redis Cluster出来之前,业界普遍使用的Redis多实例集群方法。Java的Redis客户端驱动库Jedis,支持Redis-Sharding功能,即ShardedJedis以及结合缓存池的ShardedJedisPool。客户端分区方案有其自身的优点及缺点,如下表所示:
优点 | 不使用第三方中间件,分区逻辑可控,配置简单,节点之间无关联,容易线性扩展,灵活性强。 |
缺点 | 客户端无法动态增删服务节点,客户端需要自行维护分发逻辑,客户端之间无连接共享,会造成连接浪费。 |
2.2 代理分区方案
这种方案具体的做法是这样:客户端发送请求到一个代理组件(Proxy),代理解析客户端的数据,并将请求转发至正确的节点,最后将结果回复给客户端。如下图所示:
这种方案的优缺点如下表所示:
优点 | 简化客户端的分布式逻辑,客户端透明接入,切换成本低,代理的转发和存储分离。 |
缺点 | 多了一层代理层,加重了架构部署复杂度和性能损耗。 |
代理分区主流实现的有方案有Twemproxy和Codis。下面分别简单介绍下这两种方案:
2.2.1 Twemproxy
Twemproxy也叫nutcraker,是twitter开源的一个redis和memcache的中间代理服务器程序。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。Twemproxy存在单点故障问题,需要结合Lvs和Keepalived做高可用方案。
如下图所示:
客观地分析下Twemproxy这种方案,优势和不足如下表所示:
优点 | 应用范围广,稳定性较高,中间代理层高可用。 |
缺点 | 无法平滑地水平扩容/缩容,无可视化管理界面,运维不友好,出现故障,不能自动转移。 |
2.2.2 Codis
Codis是一个分布式Redis解决方案,对于上层应用来说,连接Codis-Proxy和直接连接原生的Redis-Server没有的区别。Codis底层会处理请求的转发,不停机的进行数据迁移等工作。Codis采用了无状态的代理层,对于客户端来说,一切都是透明的。虽然文字描述Codis不那么复杂,但其实具体的实现还是蛮复杂的,如下图所示:
这种方案的优势和劣势,如下表所示:
优点 | 实现了上层Proxy和底层Redis的高可用,数据分片和自动平衡,提供命令行接口和RESTful API,提供监控和管理界面,可以动态添加和删除Redis节点。 |
缺点 | 部署架构和配置复杂,不支持跨机房和多租户,不支持鉴权管理。 |
2.3 查询路由方案
这种方案怎么理解呢?简单地说,客户端随机地请求任意一个Redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个Redis节点转发到另一个Redis节点,而是在客户端的帮助下直接重定向(redirected)到正确的Redis节点。具体方案如下图所示:
这种方案的优缺点,如下表所示:
优点 | 无中心节点,数据按照槽存储分布在多个Redis实例上,可以平滑的进行节点扩容/缩容,支持高可用和自动故障转移,运维成本低。 |
缺点 | 严重依赖Redis-trib工具,缺乏监控管理,需要依赖Smart Client(维护连接,缓存路由表,MultiOp 和 Pipeline 支持)。Failover节点的检测过慢,不如中心节点ZooKeeper及时。Gossip消息具有一定开销。无法根据统计区分冷热数据。 |
3. 总结
上面三种关于Redis集群的方案,都是小弟平时在学习和积累的时候总结的。如果有小伙伴有更多的关于Redis集群的方案,烦请告知。
上一篇: netty的timeout
下一篇: Java8之简单流操作