什么是函数式数据工程?


数据建模一直是Data LinkedIn的热门话题之一。Hadoop 提出了schema-on-read策略,导致了我们之前所知的数据建模技术的中断。我们经历了一个完整的周期,即“读取模式”导致了数据湖中臭名昭著的GIGO(垃圾输入,垃圾输出) 问题,正如本Hadoop回顾中所指出的那样。

Hadoop时代之前的数据世界
让我们参考一下 Hadoop 时代之前的数据世界是什么样的:
Garner 在 2005 年预测大约50% 的数据仓库项目会失败。为什么会这样?因为数据仓库被认为是后台工作,几乎没有与产品战略相结合

日益增长的文化不匹配
到 2010 年底,我们看到软件工程采用了 DevOps 原则和敏捷开发方法。“快速构建,打破常规”的时代与“让我们开个架构会议来增加一个新专栏”是互惠的。 敏捷数据博客再一次捕捉到了软件工程进步中不断扩大的差距,以及数据管理差距如何加剧文化不匹配。

文化不匹配在很大程度上仍在发挥作用。数据仍在努力在组织中站稳脚跟,而像如何证明数据团队的价值这样的 文章说服了领导团队。

我们越接近将软件工程原理引入数据工程,我们就越能看到数据工程在行业中的影响。

函数式数据工程
Maxime Beauchemin写了一篇很有影响力的文章,函数数据工程——批数据处理的现代范例。这是将软件工程概念引入数据工程的重要一步。该原则利用了 Hadoop 的进步。

  1. 像 S3 这样的云对象存储使存储成为一种商品。
  2. 独立的存储和计算,因此两者都可以独立扩展。是的,人类的生命太短暂了,无法同时扩展存储和计算。

函数式数据工程遵循两个关键原则
  1. 可重复性——数据管道中的每个任务都应该是确定性幂等的
  2. 可重计算性——业务逻辑会随着时间的推移而变化,并且会出现错误。数据管道应该能够重新计算所需的状态。

如何实现函数式数据工程?
Max在文章中介绍了函数式数据工程概念如何推动Apache Airflow的设计原则。让我们看看如何在Apache HudiApache IcebergDelta Lake等 Lake House 系统中实施这些原则。

模式Schema分类
实现的基础是模式Schema分类。我们可以大致将模式Schema分类为

  1. 实体- 带有主键的可变数据。实体代表业务对象,如用户、产品等,
  2. 事件- 带有时间戳的不可变数据。事件表示业务活动,例如用户 A 在时间戳 Z 将产品 X 添加到购物车中。

函数式数据工程的创立原则是一切都是时间分区表。时间分区表为每个时间分区维护实体当前状态的完整副本。

让我们以用户实体表为例。典型的数据建模如下所示:

CREATE TABLE dw.user (user_id BIGINT, user_name STRING, created_at DATE) PARTITION BY (ds STRING)

# ds = date timestamp of the snapshot

典型的对象存储文件结构如下所示。【以S3为例】
s3://dw/user/2022-12-20/<all users data at the time of snapshot> s3://dw/user/2022-12-21/<all users data at the time of snapshot>

另一方面,事件在本质上是不可变的,在本质上是暂时的。user_activity表如下所示:

CREATE TABLE dw.user (user_id BIGINT, activity_type STRING, event_timestamp LONG) PARTITION BY (ds STRING, hour STRING)

# ds = date timestamp of the event
# hour = hour of the event [Assume the events are hourly pipeline]

典型的对象存储文件结构如下所示。【以S3为例】:

s3://dw/user_activity/2022-12-20/10/<all users activity data for the hour 10 on 2022-12-20>
s3:
//dw/user/2022-12-20/11/<all users activity data for the hour 11 on 2022-12-20>

实体建模
实体数据管道可以以两种模式运行。

  1. 增量快照
  2. 完整快照

增量快照
我们通常无法获得实体的完整快照。像 CDC 或事件溯源 这样的变更数据流提供实体变化时的增量视图。
在获取日期时间表分区之前,增量快照会经过几个额外的前置步骤。

  1. 将增量数据加载到具有日期时间分区的着陆区。
  2. 将增量快照合并到基表中。基表是全表快照的初始引导程序。

完整快照
完整快照是一种相对简单的方法

  1. 获取源数据的深度克隆
  2. 将日期时间版本表写入数据仓库

为什么需要实体的最新视图?
日期分区表简化了数据流水线流程;同时,实体的当前状态需要查询临时分析。每次要求最终用户在查询过滤器中添加一个 ds 分区都会导致更多的混乱。最新分区之上的简单逻辑视图大大简化了可访问性。

CREATE OR REPLACE VIEW dw.user_latest AS 
  SELECT user_id, user_name, created_at, ds 
  FROM dw.user 
  WHERE 
  ds=<current DateTime partition>;

事件建模
事件是仅附加的,消除了合并的复杂性或从源获取维度的完整快照。

可重计算性
函数式数据工程范例的显着优势之一是,它需要较少的前期建模,并支持实体和事件的时间旅行。

数据可观察性平台检测到数据质量问题或管道计算中的错误?函数式数据工程模式允许我们从截获错误重新运行以重现数据工件。

假设企业想要改变 ARR(年度经常性收入)模型?没问题。我们可以创建一个 ARR_V2 [版本 2] 管道并从日期分区的开头重新计算。它将立即提供提供不断变化的业务环境的历史视图的优势。

函数式数据工程原则不能替代任何数据建模技术。为 Kimball 或数据保险库等领域的数据集市建模是有意义的。如果我们在数据建模过程中出现问题,函数式数据工程原则允许我们重新计算或重新生成模型。