0%

敏捷数据工程实践--基于代码的复用

前文讨论了敏捷数据工程实践的相关概念。有哪些具体的敏捷数据工程实践呢?本文将分享“基于代码的复用”实践。

应用软件开发中的代码复用

在应用软件开发中,代码复用是一件显而易见的、开发人员几乎每天都在做的事情。良好的代码复用可以有效降低代码重复率,提高效率,并减少潜在的BUG。

应用软件开发中有哪些复用代码的方式呢?从代码复用的粒度上看,有两种基本的形式:

  • 定义函数,在多个地方调用此函数实现代码复用。各种编程语言均有支持。
  • 创建文件,将一系列相关元素置于此文件,在多个地方引用此文件实现代码复用。比如C语言中的include可以包含其他文件的内容。

数据开发中传统的复用方式

数据开发与应用软件开发存在一个显著不同,那就是进行数据开发时,我们不仅要关注代码还要关注数据。

数据计算成本

在应用软件开发中,有了现代CPU的支持,一般而言,一段代码的运行非常快。但是在数据开发中,我们经常会发现运行一个数据任务花费的时间甚至比开发这个任务花费的时间都长。这就导致我们不得不将很大的精力放在运行数据任务上。

我们常常小心的设计或选择算法,谨慎的优化任务运行所需的资源,仔细的比较两种不同的存储类型的性能差异,反复的在同一个数据集上面进行验证。

我们不得不这么做,因为一段性能低下的数据计算代码,可能导致10倍的运行时间延长,最后不仅消耗了大量的计算资源,还无法满足业务需求。

在应用软件开发中,这个问题没那么显著,但是在数据开发中,这个问题的重要性就凸显出来。因为我们常常需要调度上百台计算机同时进行运算,这时,计算资源的支出就将成为我们不得不关注的问题。

以AWS云服务的定价进行计算,采用AWS Glue服务做计算引擎,按照本文撰写时的官方定价,如果调度100DPU进行10小时的计算,则将花费的费用是100 * 10 * 0.44 = 440美元,也就是约3000人民币的费用!

这还只是一个数据计算任务的费用,如果我们有100个任务呢?这个费用支出确实不菲!

做应用软件开发时,我们常常说,可以用廉价的计算成本来代替较高昂的人工成本。但是这一条规则在数据开发中并不那么适用。

基于数据复用

耗费如此长的时间与金钱才能计算出来的数据,自然是一笔重要的企业资产。于是,在数据开发中,我们采用最多的复用方式是基于数据的复用。

在数仓分层设计方法中,我们常常构建可复用的数据分层,下图是一个典型的数仓分层结构。

Data Warehouse Structure

ODS贴源层作为一个可复用的数据分层,为DWD明细层公共维度层提供数据。DWD明细层公共维度层作为基础数据,为上层的众多指标开发提供数据支持。开发出来的指标数据作为一个分层,支持更上层的数据应用层数据。(此处的数据分层命名仅供参考,业界尚无统一的标准)

在实践中,我们常常需要仔细设计数据分层,在不失灵活性的同时达到良好的复用效果。

基于数据复用的问题

基于数据分层的方式进行复用应用非常广泛,但是它也存在一些缺点。

首先是灵活性较差

后一层对前一层的数据存在很强的依赖,所以,如果前一层的数据结构没有被设计出来时,就无法进行后一层的开发。
而当我们希望设计一个数据分层可以满足后一层的大量的数据需求时,这里的设计又会变得特别复杂,常常要左右权衡,花费了大量的后一层开发不愿意等待的时间。
当前一层数据构建好了之后,如果后一层需要的数据无法满足时,还不得不修改上一层的代码并重新运行计算任务。

其次是整体数据计算过程难以理解

当我们发现计算结果不符合预期时,我们往往要追溯从数据源开始的整个数据计算过程,仔细分析内部转换逻辑,才能找到问题。当存在多个数据分层时,我们不得不往下查找每一层的计算过程。而越往下越难。这通常是由于下层在设计上要保持更高的适用度,以便支持更多的上层数据需求,而这导致很多与当前需要的数据无关的计算杂糅在一起。

在分析问题时,一个较理想的情况是,和某个指标相关的ETL的全部代码都在一个文件里面,这样就不需要多个文件跳转。同时,我们也不希望有不相关的逻辑存在于这个ETL文件中,这样我们就可以专注在问题分析上。基于数据分层的复用恰好产生了与期望相反的副作用。

基于代码的复用

在这里我希望给大家介绍“基于代码的复用”这一实践。基于代码的复用方式虽然可能会由于不能共享计算资源而导致付出较大的计算资源成本,但没有上述缺点。而且,如何处理得当,基于代码的复用也可以一定程度上避免计算资源浪费。

基于代码的复用方式在数据开发中实践不太多,但却是非常值得尝试的一个方向。

在数据开发时,如何使用在应用软件开发中广泛使用的基于代码的复用方式呢?

数据库视图

大部分数据库都提供了视图机制,视图是一个虚拟的表,它本身仅仅包含了一些转换逻辑,但并没有真实的将数据计算出来并存放在物理存储中。这给我们带来了一些启示。是不是可以利用视图的原理进行代码复用呢?视图可以理解为一段代码,查询视图即是在进行代码复用。

事实上,现在的很多数据库还在视图的基础上提供了物化视图的机制,我们可以将一个视图转换为物化视图,让数据库在合适的时机将视图中的数据计算出来,从而自动的提升数据计算性能。

视图及物化视图给我们提供了非常好的灵活性,因为我们轻松的可以在基于数据的复用和基于代码的复用两者之间切换。

物化视图还在一定程度上采用基于代码复用的方式实现了基于数据的复用。

实现ETL执行驱动器

除了基于视图进行代码复用,还可以自实现一个ETL执行驱动器,由它来提供一些代码复用的机制。比如dbt Easy SQL就是这样一些开源的ETL执行驱动器。

Easy SQL提供了模板来实现类似函数级别的复用,详情可以参考这里。同时它也提供了基于文件的复用,通过Include指令可以将其他ETL文件包含到当前文件,详情可以参考这里

除了使用这些开源工具,想要自实现一个这样的驱动器也不复杂。如果我们的计算引擎是 Spark,那么我们可以使用Spark的DataFrame API,进行一些开发就可以完成。

如果有足够的研发投入,基于自实现ETL执行驱动器的方式可以做得非常智能,达到甚至超过数据库视图和物化视图的效果。一个可以考虑的方向是,程序可以自动分析所有ETL执行过程,然后用算法识别可以有较多复用的中间结果,然后自动将中间结果保存到某处。在后续ETL执行时,自动从中间结果取数据,而不是重新计算。

目前市场上还未见到此类智能的ETL执行驱动器出现,不过,在我看来,这是一个不错的研究方向。

选择哪种复用方式

在实际项目中,如何选择复用方式呢?有以下建议可以参考:

  • 某些ETL要处理大量的数据,计算过程要消耗大量的资源,且运行时间特别长,建议以基于数据的复用方式为主,就可以有效控制资源
  • 某些ETL只需要处理有限的数据,此时可以转换为基于代码的复用方式,从而获得较高的灵活性
  • 难以选择时,优先考虑使用基于代码的复用方式

总结

本文讨论了如何在数据开发过程中进行复用,提到了两种常见的复用方式:基于数据分层的复用与基于代码的复用。在实际项目中,常常需要根据具体问题,两种复用方式取长补短,结合起来使用。基于代码的复用有着非常大的优势及潜力,是特别值得探索的。

敏捷在这里的主要指导意义在于,我们无需拘泥于具体的技术形态,而是要以高效实现业务价值并控制成本为首要目标来开展数据开发。

欢迎关注我的其它发布渠道