在采访的一开始,我们先对 FreeWheel 大数据团队进行了简单地了解。
崔扬介绍了 FreeWheel大数据平台目前的三个研发方向:第一是像 HBase、Hadoop 这样的大数据平台技术;第二是数据处理流水线;第三就是崔扬所在的 Query Engine 团队,作为公司数据检索的首要入口点,支撑着整个公司的数据查询需求和服务。这三个方向都是建设数据仓库的基石。
而 Query Engine 是 FreeWheel 大数据基础架构平台下的一支技术团队。
从公司的架构设计方向来说,前两者对应用开发者努力做到透明化,Query Engine 则是有意暴露给用户的服务。公司的各个团队可以通过这一平台自由实现自己的需求, 比如建设公司的数据仓库, 构建自己的数据产品, 甚至是以交互形式做一些探索式的数据分析等等。崔扬告诉记者,团队目前有两个主要的技术方向:一是基于 Presto 架构的查询服务,二是基于 Spark Streaming 的实时流处理服务。
在 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 的方式,允许应用方通过程序调用的方式创建 / 销毁或者伸缩集群。
历经千辛万苦,数据仓库已经构建成形并已收获成效,但是,还有几个明显的问题需要继续改进。
王澍说,最主要的问题,也是很多数据仓库都会面临的一个问题是:数据模型如果比较固定,业务上的一丁点变化就会对内部造成非常大的改动,如何去权衡模型的灵活性,业务与需求之间的差异可能目前处理的还不是很好。他进一步解释说,比如说会有一张特别宽的表,但在实际使用的时候发现,集中的 Metrics 和 Dimension 都会集中在几组上,这就是过度设计的问题。
第二,基于业务特点,如果在数据回填期间出现了一些问题需要重新计算部分数据,这时候如何去更新历史上的一批数据,一直是一项棘手的问题,因为发生问题的原因多种多样,需要改的东西也是连锁式的。
崔扬则认为,因为之前查询引擎重度依赖 Presto,所以很多业务上的逻辑直接集成到 Presto 的查询引擎或者查询流程过程中,这样的做法是会和 Presto 之间有一定耦合。他告诉记者,之后的思路和方向是想把这部分与业务耦合的功能模块从 Presto 剥离出来,形成一个独立的模块。这样可以让更多的查询引擎复用这部分逻辑代码。这样相当于多条腿走路,多重保障。
王澍和崔扬也简单地谈了谈他们对于构建一个优秀数据仓库的看法与经验。
王澍表示,一个优秀的数据仓库的标准不少,但是最主要的一条,是如何去权衡数据仓库设计的抽象的层次,跟实际业务需求之间变化的可能。
他解释道,想做到任何需求都可以支持满足,那整个数据仓库也许只能存最明细的数据,但是这种情况下,是不太可能保证查询的性能的,因为所有的查询都会反复计算最明细的数据。如果把数据仓库汇总到较高的层次上也许能实现查询速度上的加速,但有细微的变化,就要重新引入一条新的 ETL 流程,去计算新的需求,如何平衡这两点,是非常考验设计的。
王澍认为,建数据仓库本身不是目的,目的是为了满足应用的需求,不应该把问题局限在如何搭建数据仓库上,而应该关注如何满足应用的需求上。他举例说,有一些应用查询对于响应时间的要求非常严格。例如用户在想要在页面上,不可能等一段时间才让结果算出来,即使数据仓库模型再合理,计算引擎的效率非常高,算出了很多东西,但应用阶段并不关心这些,用户要一秒看到结果。所以在做数据仓库的建设的时候,每一层应该有什么样的存储结构、什么样的查询引擎,都是非常重要的。
崔扬也同样认为,优秀的数据仓库首先一定要满足业务的需求,其次是数据仓库一定要是分层,以满足不同的查询范围,或者查询的实时性要求。此外,底层的支撑框架或者支撑引擎应该多样化的,数据仓库也要足够开放,可以让用户去按照自己的需求、给用户一定的自由空间去做一些定制化的事情。
在问及团队今后的方向和目标时,崔扬说:
“目前虽然团队取得了阶段性的成就,也获得了公司及业内的认可,但是,团队仍有很长的路要走,技术更新换代太快,追求极致的道路是无止境的。未来,我们计划会引入分布式缓存系统,与 Presto 结合以缓解目前使用 S3 已经遇到的性能瓶颈问题。
我们正在升级我们的 Presto 版本到最新的 0.214, 届时会享受新版本给我们带来的更多的新特性以及更好的稳定性。同时,我们也在探索多样化的查询引擎,现在 OLAP 领域内已经涌现了非常多的 SQL on Big Data 的优秀框架, 我们在不断调研和尝试新的技术,以期能与目前的技术框架互补。Presto 是一个相当优秀的计算框架,它仍是我们未来重要的组成部分。我们也会积极回馈 Presto 社区, 一起参与到 Presto 生态的共同建设之中。”
FreeWheel 还有大量岗位招聘中,也包括实习生岗位,欢迎大家发邮件到 ta-bjo@freewheel.tv 了解更多。