Redis在3.0版本前只支持单实例模式,虽然Redis的开发者Antirez早在博客上就提出在Redis 3.0版本中加入集群的功能,但3.0版本等到2015年才发布正式版。
各大企业等不急了,在3.0版本还没发布前为了解决Redis的存储瓶颈,纷纷推出了各自的Redis集群方案。这些方案的核心思想是把数据分片(sharding)存储在多个Redis实例中,每一片就是一个Redis实例。
包括:客户端分片、代理分片、Redis Cluster
客户端分片是把分片的逻辑放在Redis客户端实现,(比如:jedis已支持Redis Sharding功能,即ShardedJedis),通过Redis客户端预先定义好的路由规则(使用一致性哈希),把对Key的访问转发到不同的Redis实例中,查询数据时把返回结果汇集。这种方案的模式如图所示。
一致性哈希算法分布式系统中常用的算法。
什么是一致性 hash 算法:
一致性哈希算法也是使用取模的方法,普通哈希算法取模算法是对服务器的数量进行取模,而一致性哈希算法是对 2^32 取模,具体步骤如下:
1.一致性哈希算法将整个哈希值空间按照顺时针方向组织成一个虚拟的圆环,称为 Hash 环;
2.接着将各个服务器使用 Hash 函数进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,从而确定每台机器在哈希环上的位置
3.最后使用算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针寻找,第一台遇到的服务器就是其应该定位到的服务器
一致性哈希算法问题:hash 环的倾斜
一致性哈希算法在服务节点太少的情况下,容易因为节点分部不均匀而造成数据倾斜问题,也就是被缓存的对象大部分集中缓存在某一台服务器上,从而出现数据分布不均匀的情况,这种情况就称为 hash 环的倾斜。
hash 环的倾斜在极端情况下,仍然有可能引起系统的崩溃,为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点,一个实际物理节点可以对应多个虚拟节点,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大,hash环倾斜所带来的影响就越小,同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射。
比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如mod(key,d),key是数据的key,d是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了。
一致性哈希算法解决了普通余数Hash算法伸缩性差的问题,可以保证在上线、下线服务器的情况下尽量有多的请求命中原来路由到的服务器。
如Jedis的Redis Sharding实现,采用一致性哈希算法(consistent hashing),将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。
采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。
优点:
不足:
客户端分片有一个最大的问题就是,服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。如果能把客户端分片模块单独拎出来,形成一个单独的模块(中间件),作为客户端 和 服务端连接的桥梁就能解决这个问题了,此时代理分片就出现了。
代理基本原理是:通过中间件的形式,Redis客户端把请求发送到代理,代理根据路由规则发送到正确的Redis实例,最后代理把结果汇集返回给客户端。
代理分片有:Twemproxy、predixy、codis 。
Twemproxy由Twitter开源的Redis代理,其基本原理是:通过中间件的形式,Redis客户端把请求发送到Twemproxy,Twemproxy根据路由规则发送到正确的Redis实例,最后Twemproxy把结果汇集返回给客户端。
Twemproxy github地址:https://github.com/twitter/twemproxy
Twemproxy通过引入一个代理层,将多个Redis实例进行统一管理,使Redis客户端只需要在Twemproxy上进行操作,而不需要关心后面有多少个Redis实例,从而实现了Redis集群。
Predixy 是一款高性能全特征redis代理,支持redis-sentinel和redis-cluster。
Predixy github地址:https://github.com/joyieldInc/predixy
predixy完美的实现了对redis单例模式及集群模式的支持,几乎完整的实现了redis原生的所有用于客户端的命令。多key命令、列表阻塞操作、发布订阅、脚本、扫描等高级功能全支持,在使用redis单例模式下也支持事务。
Codis 是 Wandoujia Infrastructure Team 开发的一个分布式 Redis 服务, 用户可以看成是一个无限内存的 Redis 服务, 有动态扩/缩容的能力. 对偏存储型的业务更实用, 如果你需要 SUBPUB 之类的指令, Codis 是不支持的. 时刻记住 Codis 是一个分布式存储的项目. 对于海量的 key, value不太大( <= 1M ), 随着业务扩展缓存也要随之扩展的业务场景有特效.
Codis是由豌豆荚自主研发,支持平滑增加Redis实例的Redis代理软件,其基于Go和C语言开发,并于2014年11月在GitHub上开源。
codis github地址:https://github.com/CodisLabs/codis/
Codis是如何分片的?
Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024。
每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供。数据的迁移也是以slot为单位的。
Redis获得动态扩容/缩容的能力,增减redis实例对client完全透明、不需要重启服务,不需要业务方担心 Redis 内存爆掉的问题. 也不用担心申请太大, 造成浪费. 业务方也不需要自己维护 Redis.
Codis支持水平扩容/缩容,扩容可以直接界面的 “Auto Rebalance” 按钮,缩容只需要将要下线的实例拥有的slot迁移到其它实例,然后在界面上删除下线的group即可。
相对于twemproxy的优劣?
codis和twemproxy最大的区别有两个:
相对于redis cluster的优劣?
redis cluster基于smart client和无中心的设计,client必须按key的哈希将请求直接发送到对应的节点。
这意味着:使用官方cluster必须要等对应语言的redis driver对cluster支持的开发和不断成熟;client不能直接像单机一样使用pipeline来提高效率,想同时执行多个请求来提速必须在client端自行实现异步逻辑。 而codis因其有中心节点、基于proxy的设计,对client来说可以像对单机redis一样去操作proxy(除了一些命令不支持),还可以继续使用pipeline并且如果后台redis有多个的话速度会显著快于单redis的pipeline。同时codis使用zookeeper来作为辅助,这意味着单纯对于redis集群来说需要额外的机器搭zk,不过对于很多已经在其他服务上用了zk的公司来说这不是问题:)
特性 | predixy | twemproxy | codis |
---|---|---|---|
高可用 | Redis Sentinel或Redis Cluster | 一致性哈希 | Redis Sentinel |
可扩展 | Key哈希分布或Redis Cluster | Key哈希分布 | Key哈希分布 |
开发语言 | C++ | C | GO |
多线程 | 是 | 否 | 是 |
事务 | Redis Sentinel模式单Redis组下支持 | 不支持 | 不支持 |
BLPOP/BRPOP/BLPOPRPUSH | 支持 | 不支持 | 不支持 |
Pub/Sub | 支持 | 不支持 | 不支持 |
Script | 支持load | 不支持 | 不支持 |
Scan | 支持 | 不支持 | 不支持 |
Select DB | 支持 | 不支持 | 支持 |
Auth | 支持定义多个密码,给予不同读写及管理权限和Key访问空间 | 不支持 | 同redis |
读从节点 | 支持,可定义丰富规则读指定的从节点 | 不支持 | 支持,简单规则 |
多机房支持 | 支持,可定义丰富规则调度流量 | 不支持 | 有限支持 |
统计信息 | 丰富 | 丰富 | 丰富 |
结论:
在功能的对比上,predixy相比另外两款代理更为全面,基本可以完全适用原生redis的使用场景。在性能上,predixy在各轮测试中都以较大优势领先。对各代理的总结如下:
可以查看另外一篇文章:Redis核心技术-高可靠-集群(cluster)
可以查看其它文章:理解并从头搭建redis集群