跳转到主要内容

热门内容

今日:


总体:


最近浏览:


Chinese, Simplified

在Data Minded,我们观察到近年来dbt在我们的客户中越来越受欢迎。虽然它主要用于数据仓库(DWH)之上的数据转换,但我们也看到了将dbt与Duckdb一起使用作为Spark在数据湖之上进行数据转换的替代方案的潜力。

data pipeline

为什么dbt在数据管道方面胜过Spark?

十多年来,Apache Spark一直是执行数据转换的首选。然而,随着云数据仓库在过去五年中越来越受欢迎,dbt迅速获得了吸引力。事实上,Data Minded的首席执行官Kris在两年多前就写过一篇关于dbt和Spark的博客。它的标题是“为什么dbt有一天会比Spark更大”。在我看来,dbt相对于Spark的主要优势在于:

更广泛的目标受众

为了使用dbt,您主要需要了解SQL。由于SQL已经存在了40多年,许多人以前都使用过SQL,因此可以理解/编写/维护dbt代码。此外,您不仅限于数据工程师(Spark通常是这样),而且数据科学家、数据分析师和BI开发人员在维护dbt项目时也会感到舒适。

简单的执行

虽然我喜欢Spark编排我的数据转换,但我必须承认,它可能是一个复杂的野兽有很多配置选项和陷阱。在我的团队中加入新的数据工程师时,我一次又一次地注意到了这一点。它们从一个正在工作的管道开始,对连接的顺序进行一个小的更改或更改配置设置(例如spark.sql.shuffle.dartitions、spark.sql.autoBroadcastJoinThreshold),然后管道突然失败。大多数时候,这是因为他们还不了解Spark是如何分配计算的。

Dbt极大地降低了数据工程师的复杂性,因为它将计算负担转移到了DWH(例如Snowflake、Redshift)。这意味着,大多数用户不需要了解DWH中查询的低级别执行,除非他们想优化特定查询的性能。将所有计算推送到DWH的一个缺点是,云数据仓库处理数据的费用通常比其他执行引擎更高。

大多数公司没有大数据

Spark的伟大之处在于它使您能够处理数TB的数据。它通过将计算分布在多个节点上来实现这一点,但在使用中型数据集(10–100 Gb)时,这种协调/通信方面可能会带来巨大的开销。在过去5年的数据工程师工作中,我注意到我90%的数据管道只处理中型数据集

此外,云计算的兴起使具有256 Gb RAM的虚拟机变得更加容易获得,这就引出了一个问题:

如果我们的数据集只能放在一台机器上,我们是否需要像Spark这样的复杂分布式系统来处理我们的数据处理?

在过去的几年里,出现了许多试图填补这一空白的新技术(例如 polars, pyarrow ecosystem生态系统等)。它们通过在单个节点上运行来简化执行,但仍然需要编写Python代码。这限制了可以维护它的人数,因为我们都知道数据工程师是一种稀缺资源。所以我想知道,我们能做得更好吗?

Dbt和Duckdb前往救援

起初,我对将dbt用于数据管道持保留态度,因为它需要将我的所有数据存储在DWH中。虽然这对结构化数据来说不是什么大问题,但它不太适合半结构化数据。在过去的一年里,DuckDB,一个正在处理的OLAP数据库,得到了相当多的关注。它可以被视为相当于云数据仓库的单个节点

当我发现由Josh Wills正在开发的dbt duckdb适配器时,我的兴奋之情与日俱增,我终于明白了模糊是怎么回事。将duckdb与dbt相结合可带来以下3个好处:

无需依赖DWH进行所有处理

云数据仓库令人印象深刻,但如果您想将其用于所有数据转换,也可能会变得非常昂贵。Duckdb在没有外部依赖的情况下独立运行,因此您可以轻松地将其嵌入任何Python或Java程序中。这使得它在许多情况下都是理想的候选者,从本地测试到处理管道中的dbt查询。

与外部存储无缝集成

Duckdb支持将数据写入外部系统,并以自己的数据库格式存储数据。它本身也支持常见的文件格式(例如CSV、Parquet)。

Duckdb的外部存储功能使得使用dbt和Duckdb将Spark中编写的数据管道无缝迁移到数据管道成为可能。这意味着您可以重写处理,同时保持作业的输入和输出相同(例如blob存储中的镶木地板文件)。

无需更改您的数据平台

在我之前的许多项目中,我遇到了一个具有类似构建块的数据平台,用于实现数据管道:

High-level components in a data platform for batch data processing.

使用商品硬件获取和转换数据,然后将结果存储在数据湖中。如果有特定需要将数据保存在DWH中,您可以将结果推送到DWH,而不是数据湖(例如,允许在Tableau或PowerBi中查询…)。

有了dbt和Duckdb,您现在可以在以下场景中使用Spark作为处理引擎:

  • 您处理中小型数据集:十分之一到百分之一GB的数据。
  • 您的转换相对简单(例如,清除输入数据或重命名列等)。

带Duckdb的Dbt比Spark快

我想研究的最后一个方面是,与Spark相比,将DBT与Duckdb一起用于在中型数据集上执行数据转换是否更便宜。为了比较两者,我使用了TPC-DS基准测试,这是比较数据库性能的行业标准。

测试设置

  1. 为了生成Spark的数据和运行性能基准测试,我使用了我在讨论Spark性能改进的博客文章中详细描述的方法。TL;DR我使用Databricks工具包及其测试框架,并使用Data Minded的产品Conveyor在Kubernetes上运行Spark 3.2.1。然而,使用另一种方法在Kubernetes上运行Spark也可以获得相同的结果。
  2. 我想比较中型数据集的性能,所以我使用了100的比例因子来生成TPC-DS输入数据。这导致大约100Gb作为输入数据。
  3. 为了运行dbt基准测试,我使用了dbt1.4.0和Duckdb0.7.1基准测试。我需要重写Duckdb提供的TPC-DS查询,以便使它们与dbt一起工作,并使用S3上的parquet文件作为输入数据。由此产生的查询可以在这个git repo中找到。
  4. 我在所有的基准测试中都使用了来自AWS的相同m6.2xlarge实例。这些实例具有8个vcpu和32 Gb的RAM。此外,我连接了100Gb的磁盘存储,Duckdb用它来写内部数据库文件,Spark用它来存储shuffle数据。

后果

在结果中,我比较了Dbt+Duckdb与3种不同Spark设置的性能:

  • Spark local:此设置在单个节点上运行Spark,因此没有单独的执行器。
  • 带有1个或2个执行器的Spark:在这里,我们运行一个Spark驱动程序进程和1个或两个执行器来处理实际数据。

我只显示了TPC-DS基准测试中少数查询的查询持续时间(*)。我选择了查询40、70、81、81和64,因为它们有非常不同的查询模式;关于这方面的更多细节可以在TPC-DS的制作中找到。

| Setup         | queryName | medianRuntime (s) | minRuntime (s) | maxRuntime (s) |
|---------------|-----------|-------------------|----------------|----------------|
| dbt-duckdb    | q40       | 14                | 9              | 18             |
| spark-local   | q40       | 48                | 45             | 58             |
| spark-1exec   | q40       | 41,5              | 36             | 55             |
| spark-2exec   | q40       | 29                | 28             | 41             |
|---------------|-----------|-------------------|----------------|----------------|
| dbt-duckdb    | q70       | 21                | 21             | 28             |
| spark-local   | q70       | 24                | 22             | 35             |
| spark-1exec   | q70       | 24                | 23             | 37             |
| spark-2exec   | q70       | 27                | 25             | 34             |
|---------------|-----------|-------------------|----------------|----------------|
| dbt-duckdb    | q81       | 8                 | 8              | 8              |
| spark-local   | q81       | 13                | 12             | 24             |
| spark-1exec   | q81       | 14                | 13             | 23             |
| spark-2exec   | q81       | 9                 | 5              | 9              |
|---------------|-----------|-------------------|----------------|----------------|
| dbt-duckdb    | q82       | 14                | 14             | 17             |
| spark-local   | q82       | 48.5              | 47             | 57             |
| spark-1exec   | q82       | 38                | 37             | 47             |
| spark-2exec   | q82       | 21                | 21             | 32             |
|---------------|-----------|-------------------|----------------|----------------|
| dbt-duckdb    | q64       | /                 | /              | /              |
| spark-local   | q64       | 281               | 275            | 302            |
| spark-1exec   | q64       | 137               | 137            | 154            |
| spark-2exec   | q64       | 266               | 262            | 275            |

根据结果,有两件事很突出:

  • 向Spark中添加更多的机器会带来更快的结果。这是意料之中的事,因为计算现在可以分布在多个实例上。
  • 除了查询64(**)之外,Duckdb对所有查询都更快。这可以通过Spark在需要处理比1台机器的内存大得多的数据时大放异彩来解释。这就是为什么在单个节点或只有一个执行器的情况下运行Spark不是很有效的原因。另一方面,Duckdb针对处理单个节点上的数据进行了优化。

(*)我不考虑启动Kubernetes pod/节点所需的时间,而是只考虑执行查询所需的速度。与dbt相比,端到端运行带有2个执行器的Spark管道可能会有更差的性能,因为我们需要创建多个pod(提交器pod、驱动程序pod、1-2个执行器pod)。

(**)查询64是基准测试中最重的查询,因为它将两个最大的事实表连接在一起。尽管Duckdb在无法分配更多内存时会将数据刷新到磁盘,但由于内存不足,我无法成功执行查询。在尝试使用更大的实例时也发生了同样的事情。我需要更详细地调查为什么会出现这种情况。

结论

在这篇博客文章中,我从dbt相对于Spark提供的优势开始,比如更大的SQL受众和更简单的执行。这些好处,再加上大多数公司不处理大数据的事实,引发了人们对Spark是否是他们的最佳工具的质疑。

作为一种替代方案,我展示了当数据集不太大,并且工作负载与SQL兼容时,dbt与Duckdb相结合是非常好的。

最后,我通过运行TPC-DS基准测试,证明了dbt和Duckdb的价值。结果表明,除了一个查询外,带有DuckDB的dbt在所有查询上都优于Spark。

本文地址
最后修改
星期日, 五月 7, 2023 - 22:06
Article