作者:邹建平
随着互联网业务的快速发展,基于传统关系数据库的数据存储方案暴露了系统架构可伸缩性差、海量数据下性能存在短板、数据模型过于复杂并难以调整等问题,而关系数据库所提供的强一致性、事务性、关联操作在许多互联网应用模型中并不是必须的,所以在新的场景下,去关系化的NoSQL数据库应运而生。NoSQL数据库具有高扩展性、高性能、灵活的数据模型、高可用等特点,其中的一大分支是key-value数据库,key-value数据库采用键值对的形式来组织、索引和存储数据,具有简单、易用、可扩展性好等特点。
而Redis作为key-value数据库里的最热门的一员,在保持key-value数据库的简单快速的优点基础上,具有一些部分关系数据库的优点,例如数据结构丰富、操作原子性等特点。
推出后具备诸多优点得以广泛流行,目前高居kv数据库热度榜第一。
首先,Redis的数据全量保存在内存中,采用单线程无锁事件驱动的方式进行服务处理,协议上支持流水线批量和增量操作,无需像memcached那样取出全量数据进行操作,所以具有相当高的性能;
再次,Redis提供多种键值数据类型来适应不同场景下的存储需求,并借助高层接口使其可以胜任诸如缓存、消息队列系统、排行榜、计数器等不同的业务场景,借助单线程工作方式,甚至支持部分的事务特性;
最后,Redis采用可读易懂的协议接口,并支持了几十余种语言的客户端库,对开发者来说简单易用,开源生态也比较活跃,目前大量公司采用它来作为缓存或者存储系统,2015年以来大部分云服务提供商都提供了相关存储服务。
虽然Redis在数据结构和接口上简单易用,但在业务实践和运维过程中,还是存在不少的问题:
业界也有一些解决方案来解决redis单机版的扩展性问题,例如twemproxy和codis,他们采用了代理节点进行数据分片的方式来连接后端多个redis服务器,而redis cluster采用了去中心化的方式组成redis服务器集群。
但这些方案基本上都是在单机版的基础上实现了分片机制,而并没有解决redis单机版本身存在的问题,主备同步机制仍然借助RDB、AOF等机制,容易造成性能颠簸;另外,这些集群版在运维能力的建设方面也比较缺乏,例如监控、上报、单系统的多实例化、运维自动化等。redis cluster的去中心化方式,虽然少了代理节点一跳,但需要更改客户端代码,而且该实现方式还不够成熟,业界还缺乏最佳实践。
3.1. CRS综述
CRS (cloud redis store)是腾讯云推出的兼容开源Redis协议的分布式云存储产品。该产品基于承载QQ日均万亿请求的Grocery存储平台,在Grocery原有的高可用、高可靠、高扩展性的分布式数据引擎框架基础上,引入了Redis的数据结构和协议支持,实现的一个分布式Redis存储平台。
CRS突破了单机版容量限制,能为客户提供海量的存储,支持单实例多达上百TB的存储量;接口上兼容开源Redis协议,业务代码无需更改就可以轻松接入;在Redis分布式解决方案中独创地支持分布式事务处理能力和批量处理命令;并提供了方便易用的平滑迁移工具,能够将用户原有的Redis数据无缝迁移到CRS;便捷易用的控制台,能够方便实现多维度监控、数据导入导出、无停服扩容等管理运维操作。
CRS依托于支撑QQ海量用户的Grocery存储平台,支持5个9高可用的跨IDC、跨地区的多拷贝部署机制,以及可持久化存储,数据存储稳定可靠;所有节点都无单点故障,能够在数秒内进行故障屏蔽与恢复服务;具有高可靠的备份和流水机制,能够按需将全实例或部分数据恢复到任意时刻的状态;提供内存与SSD两种存储介质,并支持两者的自动冷热分离功能,为客户提供更低成本的存储方案。
3.2. CRS与业界产品的比较
3.3. CRS架构设计要点
CRS系统将数据的存储和用户的接入分开,同一个用户的数据分布在多台机器上,从而突破单机内存容量的限制;同时,多个用户的数据,保存在同一台机器, 通过一定的策略,隔离多个用户,避免用户之间相互影响。 如下图所示,整个系统包括如下几部分:
•在线存储系统: 接入集群、存储集群和导入导出服务;
•数据高可靠系统: 主备同步模块、流水系统和冷备中心;
•运维监控系统: 日志中心和多维监控系统;
•支持系统: 任务中心、配置中心和路由系统。
其中核心在线存储系统是基于Grocery存储平台进化而来。
下面先从Grocery的业务现状、历史版本变化、技术特点再到redis引入的改造优化技术点来介绍CRS的架构特点。
3.3.1. Grocery介绍
Grocery是专门针对即通业务数据轻、访问量大、容灾要求高等特点,在QQ资料系统、QQ关系链系统基础上开发的更抽象、更通用的NoSql存储系统,具备数据类型和操作接口丰富、可多地部署、可在线遍历等多种优势。
当前Grocery已经在深圳、天津、上海、加拿大部署了4000+机器,承载了300+业务,总数据存储量达到250T,每秒峰值访问量达到4500万次,每天访问量达到2.6万亿次。从服务启用至今,平均服务可用率达到了99.999%。
从2011年11年发布了Grocery第一个正式版本到现在,Grocery经历了几次大的优化里程碑:
2012年4月,支持了批量操作、对用户侧API进行了优化,并通过合并写binlog提升了写性能;
2012年8月,支持了跨地区的异地部署能力;
2013年6月,支持平滑扩容与多系统间平滑迁移运维能力;
2013年底,支持SSD存储介质的Grocery版本发布;
2014年6月,支持全球部署,并有登录相关业务实现了深圳、天津、上海、加拿大四地多拷贝部署;
2014年底,实现了多集群自动化管理平台,能够一键实现部署、扩容、迁移等常见运维操作,同时也优化了自动故障处理流程,很好提升了运维效率;
2015年6月,实现了公共仓库部署模式,冷热分离,并优化了存储组织结构、寻址方式与同步机制,将单机读写性能提升了150%。
3.3.2. Grocery架构与技术特点
如下图所示,Grocery系统由接入层、数据存储层和配置运维中心三大模块组成:
接入层(interface):负责和用户的客户端直接通讯,无状态,可无限扩展;
数据存储层(data-svr):负责存储用户的数据,支持多拷贝之间的同步,以及平滑扩容;
配置和运维中心(config-svr):负责管理、下发配置信息,管理、调度运维指令,以及告警监控等功能。
3.3.2.1. 数据分布方式
Grocery中存储的数据按主key在集群中以一致性hash算法进行分布,一致性hash算法拥有无限平行扩展、消除热点、最小化迁移量等优点,算法根据主key的hash值,将其落在某台机器所属的虚节点上,以此来决定其所在机器,示意图:
Grocery使用自研的改进割环算法来决定各机器节点在环上的位置,根据此算法的计算结果,可以在保持每台机器只有20个虚节点的前提下,支持至5000台服务器,同时保证各机器key数量和负载的最大偏差<5%。
3.3.2.2. 同步机制和异地部署能力
Grocery的数据可同时存在于多个拷贝中,采用一主多备的结构,最多支持12个拷贝(1主11备)。数据的所有改变由主机实时同步给备机,支持增量同步、全量同步以及全量数据恢复等同步机制,备机可分摊读请求,支持异地部署以提高客户读请求的响应,非常适合读多写少的场景。主备集群的状态由运维中心统一监控管理,在出现同步差异过大、死机等情况下可实现秒级的状态切换和服务恢复。
3.3.2.3. 数据解析插件机制
Grocery在传统的Key-Value存储基础上,对Value做了各种数据结构的扩展,其中数据结构和其存储引擎是分离的,即Grocery系统框架部分只负责数据的存储、落地、同步、扩容等基本操作,具体到涉及Value的结构的操作,则是通过编写插件完成,只需要实现几个简单的接口并向系统注册动态库,即可给Grocery增加新的数据结构处理功能,例如,我们可以让Grocery对外提供Redis、MongoDB甚至SQL接口进行访问,而不需要改动它本身的主体引擎代码,只需新增几个动态库即可,插件机制给了Grocery极强的功能扩展能力,将Grocery和其他数据库的特性有机地结合在一起,也给使用其他数据库产品的用户接入Grocery提供了很大的便利。
3.3.2.4. 数据存储介质
在Grocery中,不但存储引擎和Value数据结构是分离的,数据处理逻辑和具体的落地方式也是不耦合的,Grocery逻辑处理只关心抽象数据本身,具体数据的存放则交给下层的库来做,因此理论上数据可以存放在内存、磁盘甚至远端的其他数据库引擎,目前实现了存放于内存和SSD磁盘的落地方式,配合数据的冷热分离算法,可在保证足够的访问性能前提下极大压缩成本。
3.3.2.5. 冷热分离
因为业务场景的关系,用户的数据,总会有访问密度的差别,某些数据比较“热”,可能会连续多次的读写访问,某些数据比较“冷”,可能会几天都得不到访问。鉴于上述情况,如果将用户数据完全部署在内存版本CRS,系统成本代价大,降低了资源有效利用率, 反之,如果将数据都部署在SSD, 可能导致访问时延加大,服务体验变差。为了取得“性价比”与“服务质量”的平衡, CRS启用了“冷热分离”的混合部署方式。
一旦启用冷热分离模式, 系统会按照预置的“冷热规则”,开始自动的识别“筛选”数据,将热数据迁移到高效的内存系统,将冷数据沉降到SSD系统。而“冷热规则”,可以根据需要,实时动态调整门限,以达到系统容量与性能的平衡。
3.3.3. 基于Grocery的Redis解决方案
3.3.3.1. Redis数据插件
利用Grocery的数据解析插件机制,可以很容易给Grocery增加Redis数据接口和存储:
如上图所示,我们在数据机嵌入了Grocery-Redis数据插件,可直接解析Redis协议,而下方Grocery通用存储层则完全不感知Redis数据格式,只是按照Cache的命令以一定格式将序列化的数据落地,并支持同步等操作。
数据机在处理Redis协议时,先将数据从存储中通过Redis格式处理模块恢复为Redis的各种数据结构,根据从协议中收到的Redis命令对数据进行读取或修改,修改后的数据被写回到Grocery存储中,从而完成了数据存取流程。在这个流程中,Redis插件引擎通过内建Redis-cache机制,保证了数据存取效率。
3.3.3.2. 分布式事务
Redis单机版本提供多条命令事务支持,扩展到分布式系统,这里需要引入分布式事务的能力。CRS对分布式事务的支持,原理与“二阶段提交”类似,首先为确保事务的一致性,事务执行的协调者,会生成全局的事务ID, 用事务ID来锁定分布式的资源,资源一旦被锁定,仅持有该事务ID的执行过程,可以访问或修改该资源,其它访问被阻塞或丢弃。当资源锁定都OK后,可以认为资源准备成功,开始申请资源修改过程,所有操作结束后, 协调者主动发起释放锁,通知各资源提交,事务成功结束。如果修改操作失败,协调可以发起回滚操作,终止事务提交。
整个过程中,可能会遇到两种异常:链路不可达,节点异常宕机。 CRS采用了两种手段来应对, 其一是定时解锁与主动回滚机制,主要应对链路不可达引起的超时异常;另一是事务流水日志恢复机制,主要应对节点宕机引起的提交异常。
3.3.3.3. 云上多租户上报与监控
由于CRS系统中,每一台机器都会服务多个业务,需要对各个业务的各种命令进行监控,因此加入了多维上报系统。
多维上报系统提供简单高效的上报接口并支持从任意维度进行数据的上报和聚合。后端存储服务会根据业务的上报信息自动建表和分表,因此业务无需事先申请需要上报的维度,可以自由扩展。由于上报的数据量巨大,多维上报系统在客户端会对一分钟内的数据进行一次汇聚再传递到服务端做存储,以减小对服务端的压力。
目前CRS系统通过多维上报系统来生成日报,能知道各个业务每天的详细运营情况,如下图所示:
3.3.3.4. 集群化配额管理 在云环境中,机器以分布式的方式进行工作。在单机的流量、CPU、内存等资源有限、数据热度分布不均的情况下,如何对资源进行统一有效的管理与隔离,防止单一业务大量耗费资源,进而导致整个云的服务质量下降,成为需要解决的一个问题。
为此我们开发了集群化配额管理系统。系统记录了每个业务在每台机器所使用的资源的情况,并将该数据提交到调度中心。由调度中心根据该业务在每台机器的使用情况及其所能使用的资源额度,调整该业务在每台机器的配额,同时确保所有业务在单台机器上使用的资源不会超过该机器的最大能力,防止机器过载的情况发生。
3.3.3.5. 无缝迁移
在目前常见的迁移方案(如codis 的redis-port)中,业务做迁移时都需要停服,无法灰度而且在不满意新的redis方案时也不能回滚。因此CRS提供了无缝迁移模块,支持对单机redis,codis及twemproxy数据的迁入。
CRS的无缝迁移模块支持不停服迁移,业务在迁移过程中可以灰度修改配置或代码。用户还可以通过web端查看当前的迁移进度,并可以配置在何时将服务真正迁移到CRS中(数据从CRS中读取),何时下架旧的redis服务。
在迁移过程中Migrate proxy会对业务使用的命令进行校验,如果有CRS不支持的命令则停止数据迁移并通知业务做相应的修改。此外,无缝迁移模块在用户真正确认下架旧的redis服务之前会尽量保证旧的redis服务和CRS中数据的一致性,因此用户如果发现CRS有不能满足业务需求的地方,可以及时回滚。
3.3.3.6. 数据用户管理机制
CRS提供了数据导入、导出、快照和记录流水功能,在数据管理方面,形成完整的闭环解决方案,为用户数据提供健康保障。
目前提供了以下功能,满足用户数据管理的需求:
•RDB导入——将用户的RDB文件导入到Grocery
•RDB导出——将Grocery数据导出为RDB文件
•生成快照——在运行时生成用户数据快照
•快照恢复——用户选择快照恢复数据
•时间点恢复——将用户数据恢复到指定时间点
•Key级快照恢复——从指定快照恢复Key
•Key级时间点恢复——将指定Key回滚到指定时间点
基于以上功能,用户可以方便地在自建redis和grocery间迁移数据,可以根据需要生成快照,并在实例和Key级对数据进行回档操作,满足日常运维和运营需求。