2020 年初,疫情让许多创业公司紧急刹车,这无疑是一次极限压力测试。它让所有企业都知道,“黑天鹅”随时都会来,反脆弱能力很重要。

神策数据的反脆弱能力源于夯实的基本功。在过去的 5 年里,神策数据服务了 1000 余家企业。 依托底层数据采集、建模、分析、应用的标准化的用户分析体系,神策数据使得超过 EB 级别的海量数据能够高效处理,并以秒级的响应速度,服务并驱动千余家企业的发展。

期间,神策数据定义了公认的行业最高标准:30 分钟完成私有化部署、单日入库千亿条数据、亿级日活实时在线分析……至今,同行业内无一企业能够企及。

在当下的窗口期,神策数据视之为修炼内功的最好时期。复工两个月后,神策数据又一次震动行业:重构分析引擎,进入 2.0 时代!

为什么要优化分析引擎?

神策分析引擎是神策数据产品矩阵的核心组件之一,它负责神策分析中的所有分析模型的计算执行,此外,它还支撑了神策用户画像平台的标签人群的计算、神策智能运营系统中的受众选择等功能。

一般来说,它也是神策系统中最大的硬件资源(CPU、内存)占用方。因此,对它的性能进行持续优化一直是我们的工作重点。

神策数据作为一家以私有化部署为主的大数据软件服务提供商,随着客户群体在不断增加,客户的数据量级也在快速上升,目前,神策数据平台所处理的日新增数据量已经高达 1500 亿条,而神策数据的分析引擎每天处理的数据条数则在数万亿级别。

性能的持续优化一方面可以显著的提升产品使用体验的提升,而从另外角度看,也意味着我们的客户可以以更低的硬件成本来承载系统的运行。

神策分析引擎 2.0 围绕存储、查询执行、查询调度进行了全面升级与优化,下面详细介绍。

一、存储的优化

虽然我们的最终目标是为了优化查询的性能,但是数据的存储是查询的基础,因此首先我们在存储方面做了一系列的优化,其中最主要的是我们重构了事件(Event)数据的存储方案,此外我们也在数据的合并策略等其它方面做了优化。

重构事件数据的存储方案

神策数据平台中对于事件数据的存储方案在我们之前的文章中有比较详细的介绍,简单的说,我们的方案里使用了 HDFS + Parquet 来存储历史数据、Kudu 存储实时数据的方式,同时按照日期、事件来进行分区,如下图所示:

这种存储方案对于导入和大部分的查询场景都是比较友好的。但是随着越来越复杂的应用场景,我们也发现了一些需求在目前的方案下无法得到满足:

  1. 在很多复杂的分析场景下,分析引擎需要先对数据进行按照用户、时间进行排序的处理,而由于底层的事件数据的有序性很有限,这样会导致在执行查询的时候需要对数据进行临时的排序操作,消耗比较多的资源。

  2. 一个典型的应用场景里会存在多种不同类型的事件,这些事件有的需要永久保留、高频查询,而有的可能只需要保留比较短的时间周期,或者在一段时间之后就不再高频使用。

  3. 虽然大部分的事件都是对历史的记录,在入库之后就不会需要进行更新。但是依然有部分类型的事件需要支持比较频繁且实时的更新操作,比较典型的如电商的订单事件,订单的状态往往是需要可变的,如果能实现直接对状态的更新会让很多分析场景更简单。

为了解决上面几个问题,我们对事件数据的存储方案进行了一次重构,完成了以下两个主要改进点:

  1. 进一步强化了对每个分区内数据的预排序。尽可能的保证数据的有序性,这样可以极大的减少我们在实时分析时需要的重排序时间。

  2. 支持对于不同事件分桶的数据使用完全不同的存储策略(Storage Policy)这些不同的存储策略可以使用不同的存储系统、存储周期、压缩算法等。

例如对于常规的事件,我们默认使用基于本地 HDFS + Parquet 的存储方案;而对于低频使用的事件,我们可以设置定期的归档策略,把历史数据放入 AWS S3 等更廉价的存储;对于需要支持更新的事件,则采用直接基于 Kudu 的存储。

可以看到,新的存储方案不仅直接支撑了后续复杂查询效率的优化,还使得客户在海量数据下的存储成本更加可控,同时,这个全新的设计也为未来更复杂的应用场景预留了足够的灵活性。

存储相关的其它优化

支持数据的实时导入是神策数据平台的重要特性,但是在实时导入的场景下,存储系统里会不可避免的产生大量的碎片文件,而这些碎片文件则会对查询的性能有很大负面影响。

在我们之前的设计里,这些碎片文件的合并是由一个定时调度的任务来执行,这个任务会持续的使用固定的资源来进行碎片数据的合并,这一方式会导致在系统的使用高峰期占用过多的资源,而在低峰期则可能产生资源空闲。

因此,我们对它的调度策略进行了优化,使用动态的调整与执行并行度的方式,以保证在尽可能用满系统资源的同时,不影响正常的查询负载。

此外,我们还优化了主要数据的压缩算法。在经过大量的真实数据测试之后,我们发现使用 LZ4/ZSTD 的组合方案来替换之前 SNAPPY/GZIP 的方案,可以在压缩比不变甚至略有提升的同时,降低数倍的 CPU 资源使用。

图 ZSTD 官方的测试结果(https://github.com/facebook/zstd)

最后,我们还对稀疏宽表的数据的写入效率进行了优化,这个优化对于那些上千个属性的宽表的数据写入效率有数倍的提升。

二、查询执行的优化

查询执行,一直是检验系统是否健壮的试金石。

后端存储的海量数据,只有查询引擎足够强大,才能保证前端风平浪静地实时查询,整体平稳运行。

正如我们之前的文章所介绍的,神策分析引擎是以 Impala 的执行引擎为核心的系统(详情内容请参考链接:付力力:基于Impala构建实时用户行为分析引擎),因此这部分主要也是对 Impala 的执行计划以及计算层做的修改。

优化基于用户行为序列的查询

基于用户行为序列的查询是应用场景非常普遍的一类分析需求,神策分析中的漏斗分析、归因分析、Session 分析等功能都属于这一类。它们的共同点是需要得到每个用户的完整、有序的行为序列,然后进行一系列复杂的规则计算。

在我们之前的分析引擎的实现里,受限于底层的数据存储结构,这类查询每次都需要对几亿至上千亿条的数据进行重排序操作,虽然我们对这个排序操作本身已经做了比较深度的优化,但是依然是非常耗时的操作。尤其在内存资源不足的情况下,还会启用基于磁盘外部排序,这样整体的耗时会更长。

在一般的数据分析系统里,通常解决这类复杂分析问题的思路是进行预计算,即在预先定义好维度、指标的前提之下,把结果提前计算出来并缓存好。不过预计算的局限性是非常明显的,即很难应对灵活多变的需求。

因此,为了更好的支撑这类灵活的分析需求,我们依然确定了从查询执行本身来优化的整体思路,基于上文所提到的存储结构优化,在 Impala 执行层更加充分的利用了底层数据的有序性,把全局的内存排序优化为了局部的归并排序,最终使用更少的内存资源和更短的执行时间完成了查询的执行。

优化前的执行计划对比

优化后的执行计划对比

在这个优化点完成之后,部分复杂查询场景的效率提升了 10 倍,而内存使用则降低到原本的 1/5。

查询引擎的其它优化

除了专门针对用户行为序列查询的优化之外,我们还对 Impala 的代码生成(Codegen)技术做了进一步的扩展,让它在更多的场景下可以使用。

另外还实现了 Join 表达式下推的优化、针对复杂条件表达式的表达式预求值优化等,这些优化都在不同的使用场景下提升了数倍的查询效率。

值得一提的是,由于这些优化点中很多并非神策独有的场景,我们也会把这类通用的优化点都提交给 Impala 社区,其中部分已经合并到最新的官方 Release 版本中。

三、查询调度的优化

查询性能上的指标提升固然重要,但是对于神策系统的直接使用者来说,在查询性能提升同时,也更期望有稳定优异的综合使用体验。

尤其在数据量巨大、硬件资源有限的客观场景之下,不同查询的响应时间也会存在比较大的差异,但是我们依然期望可以通过在查询调度、产品体验上的一系列优化,让每位用户都能在一个可预期的时间内,及时得到正确的数据分析结果。

查询资源预估

Impala 并不是一个为高并发或者大量用户共同使用而设计的系统,尤其是在遇到大量高内存消耗查询的场景下,很容易出现集体失败的情况。而这种情况之所以出现,最主要的问题就在于查询引擎往往很难准确预估出一个查询所需要的资源,尤其是内存资源的大小。

只有有了准确的资源预估,查询的分级调度、排队、并发控制等策略才有了执行的前提。不过很遗憾的是,虽然 Impala 最近发布的几个新版本也在查询的资源预估、资源的控制方面做了不少的改进,但是依然不能满足神策分析这种复杂应用场景的需要。

不过,我们也发现并非一定需要依赖 Impala 才能获取到查询预估的信息。神策分析虽然是一个非常灵活的数据分析系统,但是在实际的应用场景下,用户的查询模式上依然还是会形成某种规律。

因此,我们完全通过对已经完成的历史查询记录的分析,结合 Impala 的已有功能,构建出了一个查询资源预估的模型。这样,我们可以在任何一个查询执行之前,对它的资源消耗做出相对准确的预估。

有了准确的查询资源预估,神策数据分析系统不但可以告知用户每个查询的大致执行时长,还可以在查询资源不足的情况下实现对查询资源的有效调度,从而避免资源挤兑导致查询连环失败的现象。

在此基础上,我们还支持对用户、角色、项目等不同维度的查询资源进行精细化控制,以满足集团型客户在资源控制方面的复杂需求。 异步查询

大部分场景下,神策分析都可以将分析结果实时返回给用户,例如在数秒或者不超过 30 秒的时间内返回并展现出结果。但在以下个别场景中,可能需要用户等待数分钟或者更久:

1) 查询的数据量特别大,同时查询复杂度很高,且无法命中缓存;
2) 查询的并发人数较多,且无法命中缓存;
3) 查询返回的结果集特别大,例如查询一个用户群的列表,返回的结果集可能有几百兆或者更大。

考虑到尽可能不阻塞用户的查询工作,且避免因误操作关闭页面导致无法找回之前的查询结果,我们在产品中增加了异步查询功能。针对上述三个场景,允许用户将此查询保留至后台持续计算。当查询完成,通过消息通知及时告知用户查看或下载分析结果。 整体性能提升对比

附上做完上面的所有优化之后,我们自己模拟的标准数据集下在一些典型场景下的性能提升对比:

神策分析引擎 2.0 是神策数据各产品线和分析模型演进与迭代的基础,本文提到的部分功能及优化点已经随着神策分析新版本的上线覆盖了数百家客户,部分底层架构改动较大的优化点则正在小范围试运行阶段,会在未来的两个月内逐步覆盖到神策数据的所有客户。

给客户带来价值,而价值源于打磨。在神策数据内部,我们视技术实力为根据地,产品的性能指标一定做到市场最佳,绝不容忍被赶上,哪怕有一丁点苗头,我们都会全力以赴,希望通过构建更强大产品性能和功能,让用户从数据中获得更深入的数据洞察力。

还想了解更多分析引擎的详细内容?点击文末链接,快去 demo 中免费体验吧(PC 端体验更佳哦)!

点击神策分析 demo,立即体验 demo~

*声明:本文章版权为神策数据所有,未经授权不得以任何形式转载,申请内容转载,请添加微信号 wafree 联系策小编