《架构师》2017年5月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

三、黄金眼计算存储技术难点

伴随着阿里妈妈业务的发展,日志数据量也在逐步增加。黄金眼系统实时处理的日均处理日志量超过100T,峰值的QPS逐步提高。同时单条日志中需要解析的核心指标也在增加,使得一条日志会转变为多条需要统计的消息,进一步造成消息量的膨胀。内部对于监控需求也越来越细化,由原来简单的大盘粒度(时间维度)一维聚合统计,变化到多维聚合统计。这进一步促使黄金眼实时计算的计算量激增。面对这么大的输入流量和这么复杂的计算场景,我们在计算存储上做了以下方面的优化。

1、实时计算如何支撑巨大的流量压力

在内部系统中,我们观察到某个任务内部出现300W/s的高峰QPS的现象,因此我们采取以下几点方式逐步泄洪,将流量高峰平稳度过。

提升拓扑处理效率——流量分层

在离线计算中常常会有数据倾斜的情况,在实时计算中也会出现同样的情况。为了解决数据倾斜,我们在实时计算的count阶段采用了了多种防止倾斜的办法。

我们在设计聚合算子是采用了2级计算模式: 2级模式的第一级采用shuffle的模式,平均分配到第一级的shuffle aggregator(SA)。在shuffle Aggregator(SA)内部中完成对group by key的内存聚合。聚合之后的key的数量已经降了n个数量级。然后再根据group by的key进行filed group分发到group by Aggregator(GBA)保证key的分发是均匀的。

在group by Aggregator(GBA)内部我们通过多个线程对数据进行读写,同时增加read cache和write batch进一步提高效率。

这样就解决了数据倾斜的问题,同时通过key的数量在内存逐步聚合,保证流量在逐级递减,使得黄金眼系统应对300W/s的流量得以实现。

提高后端读写效率——剔除无效读写

在线上系统运行过程中,我们观察到后端存储压力过大,为减少对存储访问压力我们做了一些优化工作。通过观察半结构化的日志数据,我们发现在source parse阶段处理输出的日志字段非常稀疏,这与业务访问特点有关。前端采集的日志会包含全部指标,但并不是全部指标都会有值被更新,单条日志中实际需要更新的内容只占一少部分,所以我们在cache中增加了对空值和0值的判断,去掉了大量无用读写存储的操作。

提升网络传输效率——消息压缩

面对海量的实时日志,我们首先考虑将数据处理的数据量进行了缩减。Storm是一个基于消息的实时计算引擎,在spout/bolt之间传输都需要进行序列化,反序列化。我们采用了Google的ProtoBuffer方式进行序列化,将一条日志中的需要统计处理的核心字段保留并进行传输。对于核心的数据的字段我们尽量避免String类型,而是改为对应的数值类型定义,例如timestamp=1435051454如果用string存储要10个字节,用fix32只用4个字节。根据字段的实际值把每个字段都设置了最合适的类型,这么调整一下后,实测ProtoBuffer序列化后的大小相对于之前减少了30%以上,效果明显。虽然输入的数据没有变化,但是经过瘦身后的消息在传输和计算的过程中节省了大量的网络带宽,为实时计算能力的提高打下了坚实的基础。

2、复杂应用场景如何保持schema通用性

存储系统的易用性将直接影响到上层应用使用,以及数据处理的流程,如何设计通用的存储系统是分布式系统中重要的一个环节,核心的思路是存储schema保持清晰简明。底层设计的越通用,上层才能构建出丰富的应用。黄金眼经历了三次schema结构的演变,是去繁从简的过程。下面介绍这三次schema的变化。

(1)第一版设计:业务驱动

第一版设计是每个业务按月建表,由程序定时触发按月建表,每张表的schema大致如下:

使用过程中发现metric存在数据倾斜问题,造成HBase的写region过程存在热点影响查询性能。这里的优化点是新建表的region范围按照上个月的region分布来创建,可以缓解写热点造成的影响。

(2)第二版设计:解决热点问题

第二版设计对第一版做了稍微改动,主要是为了解决热点问题,表的schema设计如下:

稳定运行了一段时间后,我们也在思考如何可以让黄金眼更高效的接入业务,于是我在存储层面重新设计时间序列框架theia, Theia是基于HBase存储时间序列数据的应用,提供多种metric数据采集方式,处理,存储,报警及展示功能的整体解决方案,黄金眼主要使用了theia的存储功能。

(3)第三版设计:通用schema

第三版我们重新梳理了黄金眼的业务场景,以及对存储的访问模式,结合业务特点重新设计了HBase的schema,取消了按月建表,所有业务可以共用同一份表,表存储分为meta表和data表,表结构大致如下:

meta表

data表

由于theia支持long、bytes、pb格式的value类型,所以qualifier中会标明value类型。第三版的设计参考的opentsdb的设计,同时避免了热点问题以及查询性能不稳定等问题。同时所有业务线使用同一份表,通过namespace区分业务线,简化了数据处理和访问的复杂度,同时也减少了运维成本。

3、一致性问题填坑——多线程get/put不一致

一致性问题一直是分布式系统中遇到的让人头疼的问题,在黄金眼中最终数据写入存储是通过类似HBase的Increment操作完成的,但由于使用HBase的Increment操作性能上较差需要堆大量存储资源,所以经过对比我们最终采用了get/put的方式来做累加操作。在黄金眼使用的初期,我们观察到监控的曲线图上曲线经常出现毛刺现象,如下所示:

通过与离线数据进行对比发现不一致情况确实存在。分析原因得知数据经过mapper处理过后会shuffle到merger的不同writerQueue中,由于hash规则使得相同的key被随机分配到了不同的Writer Queue中,key在更新过程中需要从本地缓存或者HBase中获取数据的最新状态,导致相同key的更新操作会随机出现在多个writer Queue中。找到问题原因后我们调整了数据处理方式,保证相同key的状态更新由同一个writer处理,最终解决了数据不一致的问题。

经过上述的几点优化后,系统的整体性能得到了大幅提升。同时随着系统的稳定运行,我们还在探索新的计算框架,在storm原语之上构建sum/join等实时算子会带有一定的局限性,我们目前也在调研Flink相关的计算平台,统一实时、离线的变成框架,同时进一步提高我们的系统效率,为业务提供更优质的服务。