2015 年 12 月 19 日 ,又拍云架构运维大会深圳站在科兴科学园举办举行,腾讯高级运维工程师赵乐任在会上作题为《腾讯分布式NoSQL集群运营实践》的分享,以下是演讲实录:

image.png

我目前在腾讯社交网络运营部平台技术中心担任数据运营工程师,一直从事分布式数据库和关系数据库的运营工作。

腾讯分布式 KV 存储介绍

image.png

目前腾讯的 KV 存储有三大组件:

  • 一是 CKV,在社交网络、公共服务用得非常广泛,像 QQ 空间、相册、音乐等;
  • 二是 Grocery(杂货铺),该组件是为了满足QQ的存储发展而来的。它和 CKV 相比具有更好的异地容灾能力。QQ 业务对数据的高可用性是一个关键需求。目前我们推出了插件方式支持 Redis 的 Cloud 的版本 CRS,在 Grocery 高可用基础上扩展了对 Redis 支持,可以在腾讯云官方了解更多信息。
  • 三是 Quorum,是另一个非常大的 KV 组件,使用腾讯微信社群。

社交网络存储业务类型有很多,比如 IM 类型(QQ),赞以及 feeds,另外群相关业务存储体量非常大。效果广告平台产品如广点通,用户点击广告后,如何对广告组做计费,转化为用户的实际转化率等。近期微信和手 Q 在推红包,相信大家都会深有感触,这类业务是属于热点。另外推送类业务像像红点、游戏推广。

我们存储服务器超过一万台,实现了四地部署,业务层和存储层相互配合实现在 IDC 之间灵活的切换,内存存储量超过 140TB。我们如何保持平行扩展和数据高可用,分布式存储件在具体的实现上存在差异,但总体思路是相似的。以 CKV 为例,我们知道海量的服务用单机性能支撑是不可能的,数据和逻辑层不一样地方是有状态,那么我们是如何保证单点异常的情况下服务是可用的,或者减少异常期间服务不可用时间?

这是 CKV 的架构,主要分为两个层级,一是接入层,接入层做的事情很简单,它持有路由并且分发请求,接入机没有状态,在一个仓库里,接入机只需要有相同的信息就可以了。

当逻辑层请求一个 KEY,接入机知道它应该走向哪个存储或是写到哪个存储上。由无状态接入机负责分发、分片和路由。Cache 层只需要负责数据存储,主和备 cache 作为容灾,并提供恢复数据的能力。除了以内存 mem 方式外,CKV 架构还有 SSD 方式的二级存储。我们知道内存单 G 成本还是不低的,所以 CKV 采用冷热分离:将请求量低(不活跃)数据下沉到 SSD。CKV 现在非常好的支持二级存储,数据在冷热层的切换不需要业务去做变更(透明化)

image.png

水平扩展能力的关键是解决数据分片,CKV 采用的是 Hash 方式。任何业务在 CKV 分配的时候都会讲数据 Hash 到特定的桶数。剩下的事情是将这特定的桶数和存储单元做映射关系。小的状态是只需要将所有桶的映射关系全部指向一个标准化的存储单元。当业务存储数据非常大,需要将这特定桶映射到更多的存储单元上。目前我们以一个 G 为小的存储单元。 Master 组件负责维护组件的状态、路由的分发。组件的状态心跳、业务 Hash 路由都要上报,在同一个仓库里,接入层使用同完全一致路由信息,不然会出现数据的异常。接入层解决了分片和路由分发的问题。

image.png

弹性是分布式存储非常重要的一部分,将原来比较小的业务扩大或是将非常大的业务缩小,都涉及到弹性收缩。来看下 CKV 如何搬迁,至小的数据是业务的一万分之一,至小基本上是一万个域。路由是数据 Hash ID,如果要搬数据的其中一个域,我要做的是将数据域和目标域串联起来。

image.png

搬迁存在三个状态:

  • 一是没有链接的情况下,数据可读可写;
  • 二是将源和目标链起来,业务的读写行为。写是先写目标,读也是先读目标;
  • 三是数据在搬迁,存在的问题是可以读写,但不能删除,因为不管先删源或是目标,无法判断数据的已知性。

三个元素:一是数据域,二是数据目标,三是数据原来所在的目标。

有些业务删除比较多,这种业务相对来说比较少。我们可以达到 30 分钟每台,一台设备提供 56G 内存存储。可控,至小的单元是 1W。

组件的高可用

image.png

腾讯有非常多接入层的经验与实践,其中非常重要的组件是名字服务,它提供两个重要功能,一是将后面的接入集群屏蔽掉。二是名字服务,它可以根据服务端的返回成功率情况做容灾,当接入设备异常,返回的成功率一定会有异常。当成功率低于某一个值时,我会将设备做隔离,我们目前经验值是 85%。网络间抖动,造成所有接入池成功率非常低,是踢还是不踢,这是名字服务里有的解决问题的策略。它会放一定的探测器请求,一直测它。当探测请求达到一定的成功率,我认为接入层是 OK 的。

image.png

主和备,主是提供服务,备是提供容灾和数据落地。它的设计思想是零 OI,如果设备异常,数据都丢了,所以要考虑主备的事情。备在正常状态下,只提供数据同步功能,还有一部分的流水做恢复。当主出现异常时,路由上是主+备+ CU number(谐音)做存储。首先是备只读,Cash 有一定的状态,第一个状态并不是不可服务,而是心跳异常,大家对分布式有了解的话,为了做策略的调整一般会有中间状态。

Master 根据一定的策略判断这个设备确实有问题,要从服务区里剔除,这是 Down 状态,可以重启切换。Down 的时间是一分钟。当 Master 认为适当了,可以进行服务,将数据做迁移。回到数据迁移大家用的非常多,我们在上架的过程中,会根据机房在接入层至少保证两台以上的接入机,刚才提到的是基于单点的容灾。

image.png

前段时间天津出现了大爆炸,对腾讯的影响是天津机房很可能出现掉链风险,它有多地容灾,就近接入。但是 QZone 和会员业务在天津是有部署的。在做 IDC 容灾时,单从组件做保证是很难做到的。

IDC 容灾肯定存在同步问题,深圳和天津的同步还是天津到深圳的同步,这跟上面一定的关系。我们认为业务容量没有变化,要考虑的是如果天津将请求迁移后,深圳、上海、天津能否支撑多余的容量,目前社交网络的容量占比是 4:2:1,这跟华南华北的用户体验有关系。我们将天津的数据层写入在深圳,同步在天津,只需要将同步的关系停掉,由业务层请求转。天津和上海,或是上海和天津之间可以不停的做切换。

image.png

这是腾讯社交网络在分布式数据增长趋势。业务需求和人均维护设备增长都十分惊人,10 年 10 个业务,50 多台的设备,现在上涨到人均 50 台上涨到 1030 台,做过运维相关的同 学都知道这意味着什么。单一个告警就可能把一个人逼疯了。

目前我们存储运维角色主要是三个部分:

  • 服务业务:保证业务能顺利使用到我们的存储和业务一起做好容量预估、IDC 容灾规划和成本优化等。
  • 维护组件:组件上架、异常处理等等
  • 运营工具建设:分布式存储运维有很多工具建设工作,我们不可能像以前那样传统的运维我们的存储组件。

image.png

我们以平台工具建设来解决爆发式的业务需求和异常处理,

我们的平台建设是渐进分层架构,向下有两个基础组群,一是运营平台,二是公共组件,我们有很多名字服务,还有网管、名字服务、统一接入等等,这是我们维护的至小逻辑单元。基于这些东西,我们可以调度更多资源来解决现网的问题。

在开发这一块,社交网络数据运营用到了两套开发框架,一是 YII,二 是 Django,流程和策略是分开的,流程将更多任务串起来。整个运营平台服务的对象有两个方向,面向业务或开发,面向的是数据运维。这是其中一个上包的系统将组件尽量标准化,差异化的维护成本非常高,将任务原子化,如安装 MySQL 等,这些都可以实现。之后我们用开发框架、任务队列等实现流程的可视化。

image.png

自动化的实现有两个大前提:

  • 一是我们要具备调度的能力,我们对我们的组件可以驱动它,做我想要做的事;
  • 二是我们要有相应的调度策略,接入式弹性和存储弹性都是可控的。

关于策略,这和业务需求有非常大的关系,均衡负载、高可用网站、容灾、碎片等方面是我们比较重要的策略。将策略做拆分后,之后的调度可能会产生碰撞,比如容灾策略,信息非常高,我们将所有的数据聚拢在 IDC 上。

这些策略出现错误的时候,我们如何解决,基于这样的思考,我们有一个策略中心或是决策中心,将策略做一个得分的拆分,对应的策略有对应的值。我们将所有的策略解析完成后,可能会生成很多任务。我们根据策略的得分模拟它,从高到低做派讯。如果从目标到源更高,还有必要搬它吗?考虑三点:一是如何实现业务调度能力,二要有相应的策略,三是如何解决策略冲突的问题。

image.png

我们觉得自动化建设解决的是生命周期,在成长期我们可以快速扩容,在存储业务的相对期可以做一个比较快的缩容,这都是非常广阔的资源。扩缩容是我们必须完成的事情。基于策略的实现,每周有 200 多起的自动扩容。缩容完成后可能是活动性业务,把缩容减掉必须和业务形成影响,我们有邮件和内部信息的推送。

image.png

我们有接近上万台的存储设备,这是一个荣耀,因为我们有这么大的体量,这都是钱,老板一看砸这么多钱在里面,如果设备的利用率比较高,那是没问题的。如何衡量这个业务的利用率是合理的呢?业务的 QPS 和存储量的比值,我称之为访问密度。大家熟悉热力图可以看到,华南地区人处于平行面积,西北的人稀疏。

我们将业务做衡量后,第二件事是如何优化,在分布式存储里优化的手段,腾讯主要有这些:

一是逻辑优化,当有些同学不会关注存储情况,会把需要和不需要的放进去,这种情况非常多。一个用户有 A、B、C、D、E、F,但数据不一定要存进去,整个存储的体量增长会随着业务逻辑往上走。

二是数据压缩,实际上用的 KV 层非常短。某些UI或是更多,长数越长了。这不是淘汰,数据有一定的时效性。备机不提供服务,没有其他任务的时候,它只负责写,所以我们在备机上引入了限制 CPU 资源做应有。

image.png

冷热分离非常重要的一点是什么样的数据可以下沉到 SSD 盘。原来是七天无访问数据作为我们的策略。并不是策略没有问题,我们在运营的过程中发现,和业务沟通,业务说不需要这么多,以 QQ 用户登录来看,有些用户经常登录,有些用户永远不登录,很难衡量。我们会有用户访问的时间轴画像,完成淘汰率,内存存储和全量存储,淘汰率是 SSD 和全量的比值。有了这样的调整后,我们数据的淘汰率提升到 84%,这一部分是非常大的数据。比如数据访问区间在 15 天左右,在 SSD 和记忆之间的穿越流量无形增加了,这是非常可怕的事情。

image.png

碎片整理,CKV 在存储时做了两层 Hash,第一层 Hash 是路由,第二层是将 Hash 的路由完全隔掉。我做 Hash 的时候有预分配的机制,Memcached 里有预分配组,预分配大的问题是预分配是不是符合业务数据。

image.png

这是我们的内存以及实际部署的架构,它有三个链加数据存储空间,第一个链是 Hash 桶,第二个是 Node,第三个是 Obj,预分配的机制存在造成了两个数据存储空间的浪费,一是碎片,当预分配空间比 item 使用空间多的时候,碎片是存在的。大家知道联是一种存储结构,这是一个存储空间的存在。这两个东西的存在对于数据空间的利用率、损耗是蛮大的。这是非常微观的东西,衡量和业务有非常大的关系,碎片是实际存在的,如何解决碎片的问题。

从碎片产生的源头做分析,我们做了碎片整理。预分配数据和业务写入的数据,空间上存在问题。对于一个业务数据来讲,如何获取比较合适的预付费空间满足要求。首先按照一定的策略做数据采样,它可以是一个固定的存储单元空间,是一个固定数量的 T。如果它很小,没问题。

如果它很大,几个空间过小肯定满足不了。我们的策略是固定存储空间,不满足存储空间的时候,我们拿一定的数量做分析。将所有采样空间的 Key 拉出来,这是可控的,将所有的数据全部填充,它会形成极少的数据存储单元。接下来将原来有碎片数据搬到这里就可以了。碎片率从原来的 11% 降到 6%。

image.png

业务除了观察容量,还要观察质量问题,大家把延迟的情况拉出来,这么多的业务都跟着设备查是不可能的。我们将请求的延迟分布做分钟利用,现在采用五分钟上报。将这些数据做聚合分析,一是请求端的返回,二是目标端的返回,三是请求端和目标端之间的返回,得到三个图。在对应的时间点里,请求的分布以及聚合的情况,可以让运维的同学很快的定位在问题所在。