Redis分布式集群几点说道
原文地址:http://www.cnblogs.com/verrion/p/redis_structure_type_selection.html
Redis分布式集群几点说道
Redis数据量日益增大,使用的公司越来越多,不仅用于做缓存,同时趋向于存储这一块,这样必促使集群的发展,各个公司也在收集适合自己的集群方案,目前行业用的比较多的是下面几种集群架构,大部分都是采用分片技术,保证单实例内存增大带来的一系列问题,下面所列出的codis方案目前正在不断测试过程中,测试过程没有展示出来,主要从以下几点出发。
测试架构和性能:
1、keepalived+haproxy故障测试
2、Zookeeper集群节点测试
3、Codis-proxy集群节点测试
4、Codis-server集群节点测试
5、脚本写入大量测试数据并模拟数据迁移
6、性能测试
下面具体介绍codis和其他几大集群方案
集群方案:
1、 主从高可用(该方案就是单实例形式,只是为了保证数据的安全,对于用户数据少,业务的前期可以采用,目前我司缓存架构就是采用该方案)
2、 客户端分片(典型代表:Jedis。自主写分片算法,代码掌握在自己手中,可控性强,但是需要专业的开发运维人员维护,技术要求和维护成本高)
3、代理分片(典型代表:Twemproxy,redis集群没有正式推出之前官网推荐的方案,也是目前使用最多的)
4、 Redis cluster(3版本推出的集群方案,历时四年之多的开发)
5、 Codis集群(豌豆荚15年开源的解决方案,开源之前其已经用了2年之多,与其同期官网推出redis cluster)
6、 各大互联网公司自主研发的集群架构,但是还没有开源,可能也不会开源
根据以上的简单介绍,下面主要解释后面三种集群方案的特点,以及针对业务的具体情况,斟酌选择合适的架构思考。
codis架构图
简单说明:1、codis-proxy提供连接集群redis的服务入口
2、codis-config管理工具,支持包括添加/删除redis/proxy节点,发起数据迁移等操作,自带一个dashboard工具,浏览器可以直观查看集群的运行状态
3、codis-server-group实现redis读写的水平扩展、高性能
4、codis-server实现redis实例服务,通过codis-ha实现服务的高可用
5、Zookeeper/etcd存放数据路由表和codis-proxy节点的元信息,codis-config发起的命令通过其同步到各个存活的codis-proxy,则zookeeper如果出问题则可能导致数据不一致的情况或者严重的会对外提供服务造成影响
说明:一切知识点来源于官方,本人经过测试一步步验证,大致总结以上几点
Twemproxy架构图
简单说明:1、proxy提供分片算法和redis服务入口,支持高可用
2、Redis提供实现实例,并且通过sentinel支持高可用
3、Redis-Twemporxy提供通知底层HA切换至proxy
4、每个层结构出现问题或者变更节点信息等所有操作都需要重新规划分片算法,则需要重启服务
Redis cluster架构图
简单说明:1、redis cluster本身集群方案,客户端可以任一连接一个节点
2、redis-trib.rb脚本为集群的管理工具,比如自动添加节点,规划槽位,迁移数据等一系列操作(ruby语言)
3、每个节点都和N-1个节点通信,所以要维护好这个集群架构的每个节点信息,不然会导致整个集群不可工作
注意:以下三大方案redis cluster基于3.0版本
Redis集群方案各参数比较
Twemproxy |
Codis |
Redis Cluster |
|||
架构设计 |
分布式CAP,牺牲P性能,而仅仅只是数据分片 |
分布式CAP,牺牲P性能,设计初衷为数据一致性 |
分布式CAP,牺牲C数据强一致性原则,追求redis最大的特点性能 |
||
设计模型 |
Proxy-based |
Proxy-based |
Gossip/P2P |
||
设计思路 |
分布式逻辑和存储引擎分开,逻辑层proxy代理,存储用的是原子redis,当每个层面需要添加删除节点必须重启服务生效(要重新利用散列函数生成KEY分片更新) |
分布式逻辑和存储引擎分开,逻辑层codis-proxy,存储用的是修改过的codis-server,这种好处是proxy层可以自动扩展和收缩,存储层也同样可以,每个层面都可以热插拨 |
分布式的逻辑和存储引擎不分开,即又负责读写操作,又负责集群交互,升级困难,如果代码有bug,集群无法工作 |
||
架构特点 |
Proxy无状态,redis数据层有状态的,客户端可以请求任一proxy代理上面,再由其转发至正确的redis节点,该KEY分片算法至某个节点都是预先已经算好的,在proxy配置文件保存着,但是如果更新或者删除节点,又要根据一致性hash重新计算分片,并且重启服务 |
Proxy无状态,codis-server分为组间,每个组存在一个主节点(必须有并且只能有一个)和多个从节点。客户端请求都是和proxy链接,链接哪个proxy都一样,然后由它根据zookeeper路由信息转发至正确节点,直接可以定位到正确节点上 |
这个结构为无中心的组织,不好把控集群当前的存活状态,客户端可以向任一节点发送请求,再有其重定向正确的节点上。如果在第一次请求和重定向期间cluster拓扑结构改变,则需要再一次或者多次重定向至正确的节点,但是这方面性能可以忽悠不计 |
||
codis独特之处 |
不支持 |
1、 有中心节点,逻辑问题交由proxy处理,codis还有个特点下层存储可以根据数据的冷热程度把冷数据暂时保存至磁盘,待其为热数据的时候又可以上线(这点我还没有测试) 2、 提供数据在线迁移的工具 比如需求要从redis或者twemproxy迁移数据至codis或者以后redis数据库中,又不想直接预热,可以借助其提供的redis-port命令行工具 |
不支持 |
||
开发语言 |
C语言 |
Go语言、C语言 |
C语言 |
||
服务启动方式 |
单进程 |
多进程 |
单进程 |
||
性能问题 |
1、 单点的话比起原子redis降低20%左右; 2、 增加PIPELINE管道提高性能 只要是代理的设计性能瓶颈肯定在其,因redis实在太快 |
1、 相当于单redis实例40%性能丢失(从最开始的版本比Twemproxy慢20%至目前比其快100%); 2、 弥补:增加proxy数量和支持多核,比起Twemproxy还是好一点,因Twemproxy最好的情况跑满一个CPU; 3、 弥补:增加PIPELINE管道提高性能 只要是代理的设计性能瓶颈肯定在其,因redis实在太快 |
没什么损失,追求的就是这个 1000个节点内拥有线性的伸缩性,和操作redis实例性能差不多 |
||
分片算法 |
Redis一致hash,当初设计好如后续变更修改(增减节点)需要配置proxy通知新的算法,重启服务
|
通过presharding采用solt槽位的形式,整个集群分为1024个哈希槽,分片算法位SlotId = crc32(key) % 1024,增减节点不需要重启服务 |
采用solt槽位的形式,整个集群分为16384个哈希槽,分片算法位SlotId = crc16(key) % 16384,增减节点不需要重启服务 |
||
所需组件 |
Redis、twemproxy(nutcracker) |
Codis、zookeeper |
redis |
||
数据一致性 |
不能保证强一致性 1、 如redis cluster第一种情况没有,设计不一样 2、 网络分裂,这种情况其有监控工具可以通知管理员某个主节点宕机,这时如果管理员切换HA(但是不提供自动提升从节点为主节点,因从节点变为主节点必须更新分片算法,重启服务),数据就会丢失,因主节点的写命令会丢失,除非再次AOF同步最后一条写命令,二者如国管理员可以判断其为网络分裂,等待网络恢复是主节点会向从节点同步写命令数据 |
强一致性 1、 数据迁移过程中数据强一致性性,因迁移都是一个个KEY原子迁移,每次操作都是给ZK发送信息,通知proxy,同时所有操作都是上一把锁,假如该期间有客户端访问,则提升访问该KEY的操作为优先操作,快速迁移至新节点,访问转移至新节点,不会访问老的KEY,如期间也可以中断正在同步的数据,也是不影响,因为redis没有回滚机制,要么成功要么失败 2、 网络分裂:因为它的设计初衷就是不考虑HA自动切换(后面添加该功能),等到网络恢复Zookeeper保证数据一致性,写命令不会丢失,所有操作都要在zookeeper上面注册 |
不能保证强一致性 比如官网给出的两种有可能丢失写命令的情况如下 1、 客户端向主节点A发送一条写命令,主节点是首先立马向客户端返回命令回复,然后再把刚刚的执行写命令同步至从节点,追求性能所致该设计 2、 网络分裂(network partition),如果时间很长导致A节点的从节点转换为主节点,然这中间可能存在客户端正在和A节点通信也就被孤立了,这样写的命令将丢失,如当网络恢复A又重新加入集群 |
||
数据的特殊安全 |
1、 比如某段时间业务数据一下爆表(内存写满),数据来不及迁移,这样的话redis会根据LRU策略,会淘汰一部分老的key,这个没办法,所以运维中当内存使用80%时应该扩容 2、 主从切换过程中有一部分数据丢失(主挂了到从切换上来丢失的这部分数据) |
||||
磁盘IO |
基于redis本身的持久化(RDB和AOF),肯定会存在数据丢失的情况 |
||||
数据的迁移 |
不可在线迁移,并且迁移完之后需要修改配置文件的分片算法,再重新启动Twemproxy服务,重新识别分片算法 |
采用sharding在线迁移数据,安全透明,可以自动平衡数据到其他节点,相当于可热插拨,不会造成响应时延(因迁移时会另外有个进程来做,数据迁移还是原子的数据迁移指令,这样迁移的话就会相当慢一些) |
在线迁移数据,动态迁移KEY值会造成响应时延(迁移数据是拷贝RDB数据文件,而且因为redis是单进程的),另外新节点solt又不自动,依赖ruby(redis cluster集群管理和分配脚本)脚本来平衡数据,无中心设计如此 |
||
水平扩容缩容(增减节点) |
Redis存储层操作,不提供自动的解决方案,并且要自己写脚本支持数据的搬迁活动,然后更改proxy哈希算法,重启服务 |
Redis存储层,都是在线操作(扩容数据迁移,缩容的话先搬迁数据至别的节点,然后下线该节点) |
没有代理和存储之分,可以在线操作(扩容数据迁移,缩容的话先搬迁数据至别的节点,然后下线该节点) |
||
主从是否必须 |
1、 没有数据复制不影响可用节点代替故障节点 2、 如果没有做备份,故障节点key全部丢失 |
1、 故障节点如果没有备份,则丢失掉该组的所有数据,但是不影响其他组的运行,不会导致整个集群坏掉 2、 如有备份节点,则把其升为主节点,集群没有影响,正常运转(需要管理员手动变更从节点为主节点,最新版本添加HA功能) |
没有主从复制的节点一旦故障,将导致整个集群不可用,无法写入或者读入任何key,无法进行数据重新分片 官网建议:至少3主3从 |
||
主从特点 |
基于redis本身的主从方案(利用发布/订阅机制,采用广播形式), 采用异步复制(asynchronous replication)的方案,无论是master端还是slave端都不会引起阻塞,但是肯定是存在数据丢失的情况 |
||||
集群设计 |
1、 proxy部署高可用(多proxy结合keepalived+haporxy) 2、 redis层设计多主多从部署 |
1、 proxy部署(多proxy+zookeeper集群方案,并且结合keepalived+haporxy) 2、 codis-server部署多组,每组部署一主多从架构 |
利用redis本身部署集群:至少3主3从 |
||
HA方案 |
Proxy层已经有了,由上面的设计,redis层基于自带的HA解决方案(sentinel),这里不阐述sentinel机制 |
Proxy层已经有了,存储层本来设计就没有考虑自动HA切换,后面根据用户强烈的要求,目前添加codis-ha解决 |
自主监控自动切换(把sentinel功能搬迁过来) |
||
故障监控 |
自带监控并告警 |
自带监控并告警 |
目前还没有提供 |
||
故障检测和故障转移时间 |
1、proxy配置文件设置:auto_eject_hosts: true timeout: 400 server_failure_limit: 3 当proxy server超时400毫且3次重试机会,则认为该proxy节点坏掉,这样故障节点从hash环直接取下,并且清理该Key信息 故障转移耗时:400*3=1200毫秒 2、如果存储层加入sentinel做HA (注意:底层redis转移故障之后,因proxy层不知道该配置信息已经变动,此时需要引入第四方工具redis-twemproxy-agent(node.js),更新Twemproxy配置,并重启) 故障转移耗时: 每个sentinel以每秒发送一次ping,配置down-after-milliseconds=2s,则主观认为下线3秒,然数量sentinel(配置文件可以配置)认可同意需要1s,再sentinel当选故障转移主持节点需要1秒,选出slave升级为master需要0.5秒,通过发布和订阅推送更新配置至其他sentinel并且配置更新需要1秒,这样总共耗时6.5秒。 |
1、 proxy层配置文件: backend_ping_period=5 则表示5秒zookeeper没有等到pong回应就认为proxy已经坏掉 故障转移耗时:5秒 2、 存储层本来不设置自动故障迁移的 (后面添加codis-ha机制) 过程:codis-ha通过dashboard监控每组的存活状况,zookeeper能够快速知道存活的proxy节点,然后休眠3秒再次监控至重试3次,大致10秒左右时间切换,但是官方建议,还是不希望使用该解决方案,因主节点有问题,立马切换从节点为主节点可能导致数据丢失的情况。推荐大家使用手动切换,做好监控,确定好数据安全然后使用web界面或者命令手动操作(因用户的强烈要求才加入该解决方案) |
Redis cluster拓扑结构是一张完全图:每个节点都持有N-1个输入TCP和N-1个输出TCP。 这里不阐述故障监控和切换的流程(比如FAIL状态、slave选主时机和cluster逻辑时钟等) 故障转移耗时(配置文件可以修改) Node_time=2 Fail_report_validity_mult=3 标记master为pfail耗时2秒,升级pfail为fail状态耗时为2*3=6秒,选主前随机延期期望为1秒,收集足够多master投票为max((2*node_time),2)=4秒 这样总共耗时13秒。
|
||
功能限制 |
1、 不支持多key操作 2、 不支持MULTI/EXEC 3、 不支持EVAL |
比较多,可以查看官方文档 |
1、 复杂的多KEY(set求并求交集)不能跨节点操作 2、 不支持MULTI/EXEC 3、 写丢失比较频繁 |
||
提供支持 |
不在维护 |
目前活跃状态 |
官网主打 |
||
客户端driver工具 |
保持不变(redis支持的都支持) |
保持不变(redis支持的都支持) |
只能支持cluster协议的客户端工具(目前官网上面说的是针对JAVA的是Jides,针对PHP的是Predis,而且不成熟) |
||
概括 |
1、 轻量级 2、 在proxy层实现一致性哈希 3、 快速的故障转移 4、 可借助sentinel实现底层HA 5、 每次变更必须重启生效 |
1、 基于zookeeper的proxy高可用,zookeeper会记录整个集群的生存状态,则需要维护好zookeeper 2、 优势为动态水平扩容,平衡数据,在迁移的时候不影响业务访问和响应时间,这点很炫,也是它主打的方向 3、 Dashboard操作降低人失误率,图形直观查看信息 4、 强一致数据(也是设计的重点) |
1、 性能好(也是设计的原则) 2、 无中心组织结构,有利有弊 3、 故障转移响应时间长 4、 有写丢失,比较频繁 |
总结:没有最好的架构,只有最合适的架构