在 Data Lakehouse 中统一批处理和流处理


最近,我们在 ALTEN 的一位客户表示希望开始从他们的操作系统中提取和集中数据。从分析的角度来看,他们的信息环境处于未开发状态。这为创建集中式分析平台留下了许多架构选项。
我们对数据处理的主要要求包括:

  • 1)平台必须具有高度的变化灵活性,支持不同类型的结构化和非结构化数据;
  • 2) 平台必须能够处理定期交付的批量数据和更改来自操作系统的数据捕获馈送;
  • 3) 处理后的数据必须是高度可审计的。

这些要求让我们几乎立刻就建议数据湖库 (data lakehouse),因为:
1) Lakehouse 在模式更改和数据模型更改方面比常规数据仓库具有更大的灵活性,并且支持任何类型数据的; 
2) Lakehouse 简化了 delta 架构的设置,其中批处理和流数据流是统一的;
3) 数据湖库中的奖章设计模式(medallion design pattern,)确保原始数据按原样存储,转换后的数据单独存储,并且每个湖库实现都支持“时间旅行”到表的先前状态。

对于数据湖库(data lakehouse)的技术实现,我们选择了Delta Lake,因为它是当时技术最成熟的。
显而易见的选择是使用 Databricks 进行 Delta Lake 实现,因为它们具有专有的 Delta Lake 功能。但是,我们不得不选择 Azure Synapse Analytics 中的 Apache Spark 池,因为它更容易与 AAD 中现有的 RBAC 权限模型集成,并且网络隔离功能更直接。(这两个功能当时在 Databricks 中通常不可用。)

最困难部分
最困难的部分是设计一个面向未来的、模块化的数据处理应用程序,它构成了我们的数据湖库的核心。我将带你了解我们在这一过程中做出的最重要的设计选择。

首先,我们选择将应用程序的处理逻辑构建为一个常规的Python项目。虽然这听起来很明显(撇开语言不谈),但现在许多组织选择以笔记本Notebooks 的形式封装他们的代码,无论是Jupyter还是Databricks。
Notebooks 笔记本也有模块化的功能,但通常有一些注意事项,违背了软件工程的最佳实践。我想到的其中两个是:

  • 1) Notebooks 笔记本的源码控制工作流程通常感觉很笨重,而且在Databricks笔记本中,需要你使用一个尚不成熟的用户界面;
  • 2) 从其他文件中导入模块是隐式的,这意味着当你导入一个Notebooks 笔记本时,并不清楚哪些函数或类被导入。后者在导入同名的对象时尤其会产生意外的行为。

因此,我们选择了围绕封装在.py文件中的模块化 "作业 "来构建我们的Spark项目文件夹,一个包含可重用函数和类的Python包,以及一个通用的 "spark-submit "Bash脚本,通过传递相关的命令行参数在任何环境中启动任何作业。(像这样构造我们的Spark项目的主要灵感,可以在这里找到)。这种设置允许你的文件使用标准的Git工作流程进行提交、审查和合并。它还允许从各种模块中明确导入代码。

其次,每个作业都遵循相同的结构,并利用Spark的结构化流媒体API,这样,批处理和流媒体作业在语法上是一样的。事实上,批处理和流式作业之间的黑白之分已经被有效地拆除了。每个作业都有自己的 "触发窗口",指定微批中包含的数据量,在一段时间内,从每天到亚秒的每个处理时间都是可能的。统一批处理和流处理作业的另一个好处是,每个作业都可以很容易地使用通用和可重用的Python模块来构建。我们选择的项目结构进一步帮助了这一点。

虽然这种批处理和流处理数据流的统一很优雅,但它仍然需要仔细平衡上述因素:延迟、成本和实时表的数量。我们收到的要求之一是,每张表都必须 "近乎实时 "地处理,以确保尽快处理来自其业务系统的CDC记录。由于近实时的定义并不是一成不变的,我们同意将其设定为最多15分钟的延迟,24小时不间断。鉴于需要处理的数据量相对较小,这使我们能够将成本控制在合理范围内。

我们能够保持低成本的另一个方法是,在同一个Spark集群上尽可能多地并行运行结构化数据流。由于近实时的定义相对宽松,而且数据量小,我们能够在同一个集群上并行运行大约70个源表的所有流。每个数据流每天都会被重新启动,以便每天更新处理任务。

限制和当前发展
尽管 Spark 的结构化流功能为简化数据平台的架构提供了绝佳的机会,但它并不是一种万能的解决方案。尤其是对于数据量较小的用例,例如我之前描述的设置,Spark 可能有点矫枉过正。并行化的开销与小数据用例中的性能优势不相称。不幸的是,我们被 Spark 束缚了,因为它是当时使用 Delta Lake 创建近实时数据湖库的唯一方法。

幸运的是,Delta Lake 技术在过去几个月中迅速成熟。处理相对较小的数据集的一种更便宜、更快的方法是使用Azure FunctionsAWS Lambda等无服务器计算服务。由于Databricks SQL已在 Azure 和 AWS 上普遍可用(以及 GCP 上的公共预览版),每个 Databricks 客户现在都可以使用 ODBC 或 JDBC 连接器将其结构化数据处理到 Delta Lake 中。将无服务器计算服务与 Databricks SQL 相结合,可以实现比使用 Apache Spark 更便宜、更轻量级的数据湖库实现。它还降低了实现事件驱动的处理应用程序而不是时间触发的应用程序的门槛,从而进一步提高了 Lakehouse 内的处理时间。
如果您的组织更喜欢开源技术,那么请留意delta-rs项目。它是 Apache Spark 之外的 Delta Lake 开源接口,包括 Rust、Python 和 Ruby 的绑定。到目前为止,该项目不支持使用 Python 和 Ruby 写入 Delta 表,但该项目正在快速推进。