在挑战中不断演进
在采访的一开始,我们先对FreeWheel大数据团队进行了简单地了解。
崔扬介绍了FreeWheel大数据平台目前的三个研发方向:第一是像HBase、Hadoop这样的大数据平台技术;第二是数据处理流水线;第三就是崔扬所在的Query Engine团队,作为公司数据检索的首要入口点,支撑着整个公司的数据查询需求和服务。这三个方向都是建设数据仓库的基石。
而Query Engine是FreeWheel大数据基础架构平台下的一支技术团队。
从公司的架构设计方向来说,前两者对应用开发者努力做到透明化,Query Engine则是有意暴露给用户的服务。公司的各个团队可以通过这一平台自由实现自己的需求,比如建设公司的数据仓库,构建自己的数据产品,甚至是以交互形式做一些探索式的数据分析等等。崔扬告诉记者,团队目前有两个主要的技术方向:一是基于Presto架构的查询服务,二是基于Spark Streaming的实时流处理服务。
为什么选择Presto?
在Query Engine团队创立之初,框架与工具的选型是经过了一番深思熟虑的。
据介绍,FreeWheel目前的主流计算和查询引擎是Presto,从大数据团队成立之初,就已经确立了使用Presto作为重度依赖工具,并一直沿用至今。那么FreeWheel为什么会选择Presto呢?
王澍告诉记者,和一般的数据仓库不太一样,Freewheel的业务日志结构非常复杂,整个Schema有各种各样的嵌套结构,很多计算经常需要从一个日志中的某一行的一个列表中展开来进行。借助于Presto,可以在一定程度上解决由Schema过于复杂带来的使用上的问题。
王澍表示,Presto的一个优点是提供了比较方便的插件式的扩展,可以实现自己的Connector,来处理如何去查询数据的这部分逻辑。比如,现在有一个比较新的Metric,但是怎么算并不是很确定,这个时候就比较适合先用Presto去实现,作为探索性的尝试,避免反复更新Schema。同时这个Connector也可以屏蔽一些由于Schema演进所带来的对应用的影响。这个时候Presto的可用性是很高的。
其次,大部分关系型数据库倾向于用索引去做优化,但是如果数据量非常大,并且经常变化的时候,利用索引优化并不是那么现实,因为在更新的时候,更新索引的开销本就不低。使用Presto,就可以换一个角度来解决这个问题。比如FreeWheel实现了一个叫Metadata Service的东西,主要目的是在执行查询的时候,辅助做一部分下推工作,从而减少查询需要扫描的数据量。
除了上述的两点,崔扬补充说,因为Presto是一款速度很快的计算引擎,也属于偏流式的数据处理框架,因此从这个角度来说,Presto也更适合FreeWheel目前的业务场景。这也就决定了,最好是可以通过类似于Presto的查询引擎直接去查询原始日志,或者基于文件去做查询引擎。
以上这些因素是FreeWheel选择使用Presto作为引擎直接去查询原始日志的主要原因。目前团队的Presto在负责提供对PB级别的原始日志的查询服务。
Query Engine平台发展到如今,一直基于Presto努力打造高效和稳定的服务。和很多其他的公司使用Presto只是用于线下的数据分析不同,FreeWheel将Presto完全应用到了线上的应用和服务,甚至提供面向客户的交互式查询产品,后端数据仓库的建设也是采用基于Presto的解决方案。
数据仓库建设过程中的难题与攻关
在崔扬看来,数据仓库是一个学科,经过多年的实践,已经有非常多的原理和相关的经验值得借鉴,但是他告诉我们:FreeWheel引入数据仓库技术并不是为了用而用。因为在实际的数据处理和查询的过程中遇到了一些问题,为了更好地支撑公司整体的服务,团队才开始正式应用数据仓库。
他告诉记者,在几年前,FreeWheel开始组建和应用大数据平台的时候,并不是依靠数据仓库整体的概念建设的,他说:“当时数据从Pipeline里产生出来以后,采用的方法是直接通过查询引擎去查询原始日志。”
但是这样的做法带来了一些问题。
首先,随着原始日志越来越大,每天会产生大规模数据。随着使用查询系统的用户和需求日益增多,其中时间跨度大的历史数据的查询又占据其中很大的比例,这样基于目前的查询引擎直接查原始日志的负担就越来越重。另一方面,公司的主要领域是广告系统。而广告又是一门很复杂的业务,这种业务复杂性就决定了用于描述业务的数据结构也是极为复杂的。
据了解,目前FreeWheel的原始日志以统一的Schema来进行管理,里面的业务实体字段已经接近1000个,还不包含经过Pipeline处理后加入的虚拟字段。这些字段间以网状的形式互相关联,很难拆分为多个Schema。
但是由于原始日志包含所有的字段和数据,而每个用户根据自己的业务场景,也许只需要查询其中的一部分字段,查询引擎也无法干净地只读取用户希望使用的部分数据和字段。即使已经利用了Parquet这种列式存储的特性,甚至基于Parquet文件建立一层自主开发的索引层(也就是前文提到的Metadata Service),也无法完全避免所有查询基于原始日志的方式带来的大量的资源浪费和性能开销。
所以,团队决定开始运用数据仓库的理论去做数据的分层。
王澍补充了一个例子。在Presto层,最开始的目的是提供一种日志级别的数据查询,但实际上对于用户来说,需求是两方面的。一方面可能会需要比较详细的数据做一些自己的分析;另一部分需求可能是要求数据的汇总力度非常非常高,比如查询某一个广告,在某一个时间段内的整体展示情况。因此,这就需要去设计一个比较合理的数据的处理流程或者ETL的流程,把比较细致的数据,汇总到合适的粒度。
在建立公司级的数据仓库的过程中,不仅需要面对传统数据仓库建设中的一些经典问题,同时,由于FreeWheel本身业务的复杂性和特殊性,研发团队还要解决一些特有问题。
从业务特点来说,FreeWheel是帮助客户去分析广告投放的实际效果,用户看到广告,肯定会触发一个事件,由于各种各样的原因,这个事件发生的时间会存在不可控的因素,从而导致延迟非常大,这个时候最大的时间窗口可能会到一个月以上。
为了回避这种复杂场景,一般公司对其的处理方式是比较简单的,即不做数据回填,也就是说,当天收到的数据日志全部会算到当天的数据统计中。但是对作为涉及广告及计费结算的FreeWheel来说,需要采取回填数据的方式,才能为客户提供精确的数据统计和付费对账。如何回填历史数据,也是团队面临的比较大的问题。针对这种场景,研发团队设计了一套流程,从而达到先把数据切分成独立的时间段,再根据每个时间段去更新,进而分段汇总的效果。
而由此引发的新问题是:当天日志数据中包含的历史数据是呈一种很强烈的衰减趋势分布的,距离当天越近的日期,数据会越多,反之数据则会更少。
回填的历史日期的数据,以独立的文件形式存在,这样就会存在非常多细碎的小文件。数据仓库的ETL任务粒度越细,小文件就越多。海量小文件带来的一个严重问题就是:Presto对其的查询性能下降会比较严重。而业务场景决定了,研发人员会很难找到一个明确的时间点,以该时间点为切点将之前日期的数据文件做离线合并成大文件的操作。
另外,FreeWheel对数据的精确度要求非常高,如果数据出现了误差,即使是百万分之一的误差,都需要重新计算。而如果一旦将分区下的文件合并在一起,就无法再简单地支持重刷某日当天的数据。针对这种场景,FreeWheel也研究出了一套兼容重刷数据而又可以合并文件的方案,可以极大提升查询的效率。
另一个存在的问题是:当数据仓库的聚合层表以及主题层表生产出来后,线上BI工具会使用Presto直接基于这两层的数据提供对外的服务。但是某些BI工具的SQL建模及优化实现得并不完美,查询性能问题比较严重。以下面的代码为例:
select t.a, t.b, sum(t.c) from (select a, b, c from aggregate where dt >= ‘2018-10-01'and dt <= 2018-11-01’ ) t left join d_table on t.a = d_table.a group by t.a, t.b
这种查询会先将一个月的数据全部读取出来,做完Join后才会做Group By。如果能将Group By下推到子句中执行,这样就可以只需要将聚合后的结果数据做Join操作,数据量会小很多倍,执行效率也会提升非常多倍。
针对此,研发团队修改了Presto的Optimizer的源码,加入了类似Aggregation Push Down的优化,总体性能有数十倍的提升。
上图中是FreeWheel针对第一款基于Query Engine的产品进行性能优化的结果。可以看到,70%的查询有10倍以上的速度提升,有20%的查询甚至有100倍的性能提升,优化的效果非常好。据介绍,目前FreeWheel对Presto优化器的改进点主要有三个:Partial Aggregation Push Down, Broadcast Join和TopN Optimization,其中后面两处优化在Presto后续的版本中也得到了社区的重视。
崔扬告诉我们,团队在不断建设贴切公司业务场景的数据仓库的实践过程中,解决了非常多的问题,也积累了非常多的经验,同时对于数据仓库未来的发展方向也有了更清晰的规划。
打造优秀的查询引擎服务
回顾Query Engine团队的发展,崔扬认为有这样三个关键的阶段:
· 2017年中的时候,团队将Presto升级到了0.184的版本,崔扬说:“这个版本相当于现在FreeWheel的Presto Cluster的基石,我们现在所有的查询都是基于这个版本来做的。”
· 2017年底到2018年初,FreeWheel团队已经开始使用数据仓库整体的建设,并完整支持了第一个基于整体大数据查询引擎的业务需求。
· 2018年年初到年中,团队把整个引擎迁移到AWS以后,结合目前公司的现状开发了一套管理所有Presto集群的服务,称为Presto Service Manager。据介绍,这套服务主要的工作其实就是帮助维护和管理整体的Presto的集群。
在长期的探索和不断历练的过程中,FreeWheel在查询引擎方面积累了大量可贵的经验,也逐渐拥有了核心的技术竞争力。
FreeWheel的数据经过Pipeline实时处理之后,会存放到HBase中,之后会提供两条分支供用户根据不同场景使用不同时间流的数据:一条分支是作为实时查询的解决方案,团队实现了基于Presto的HBase Connector,直接对HBase中的实时数据进行查询;另一条分支是以小时为粒度定时将HBase的数据转存到文件存储系统,这些文件都是以Parquet的格式进行组织。
之后研发团队又实现了基于Parquet文件读取的Presto Connector,使用Presto查询这部分历史数据。这两条分支的数据集都是原始日志,它们可以无缝拼接在一起,提供给用户以完整的数据流视图。
Presto提供实时 + 离线数据的完整数据流视图
到如今,FreeWheel的大数据平台全线迁移到云服务平台。Query Engine团队更是借力云平台独有的优势,实现和提供了更加灵活和轻便的服务。
迁移到AWS云平台后,FreeWheel为什么没有用AWS的托管服务如Athena,甚至是EMR,而是选择使用EC2搭建和维护自己的Presto集群呢?
针对这个疑问,崔扬解释说:“我们针对社区版本的Presto做了深度的定制,同时另外还实现了两套Presto Connector以满足我们的业务场景。而EMR提供的Presto版本是社区的官方版本,无法运行我们自己的定制版本,所以EMR是不能满足我们的需求的。未选择Athena也是类似的问题。当然,当最上层的聚合表生产出来后,由于数据量变得相对较小,这种场景下是可以考虑使用Athena的。不过从目前我们针对效率和开销的对比来看,我们自己提供的Presto会相较Athena来说会更合适一些。”
此外,FreeWheel根据目前的业务需求,结合云平台非常突出的弹性能力的特点,自主开发了一套管理Presto集群的服务,之后为每个业务场景独立运行和管理一个Presto集群,相当于做了业务层面的资源隔离。
王澍解释说:“我们现在的业务场景里,有十几套Presto集群,并且集群的规模和整体的数量是要不断地增长的。如何能很快速地帮用户部署一套集群,或者管理一套集群,而且能够随着用户的需求伸缩这个集群的规模?这就需要有一套服务框架能帮助我们去管理所有的服务和集群。这就是我们做的Presto Service Manager,是FreeWheel演进历程中一个里程碑式的产品。”
Presto Service Manager功能视图
Presto Service Manager实现了自动化运维集群的功能,它可以自动检测到新集群的创建和老集群的销毁,做一些注册和清理工作;同时,它可以自动检测或者推断出集群里某些Worker是否处于不正常的状态,然后自动将不正常的Worker踢出集群,并换入新的Worker进来。
Presto Service Manager还可以自动根据集群的负载情况,自动进行Scale Out和Scale In的动作,使Presto Cluster具备自动的伸缩能力以应对高峰流量,并且提供了API的方式,允许应用方通过程序调用的方式创建 / 销毁或者伸缩集群。