http://www.tuicool.com/articles/nM7zymJ
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、 有写丢失,比较频繁 |
总结: 没有最好的架构,只有最合适的架构