Apache spark 三角洲湖:upsert内部如何工作?
在我们的数据管道中,我们从数据源接收CDC事件,并以AVRO格式将这些更改写入“增量数据”文件夹 然后定期运行Spark作业,将此“增量数据”与当前版本的“快照表”(ORC格式)合并,以获得最新版本的上游快照 在此合并逻辑期间: 1) 我们将“增量数据”作为数据帧df1加载 2) 将当前“快照表”作为数据帧df2加载 3) 合并df1和df2,消除重复ID并获取行的最新版本(使用update_timestamp列) 该逻辑将“增量数据”和当前“快照表”的全部数据加载到Spark内存中,根据数据库的不同,Spark内存可能相当大 我注意到,在Delta Lake中,类似的操作是使用以下代码完成的:Apache spark 三角洲湖:upsert内部如何工作?,apache-spark,databricks,delta-lake,Apache Spark,Databricks,Delta Lake,在我们的数据管道中,我们从数据源接收CDC事件,并以AVRO格式将这些更改写入“增量数据”文件夹 然后定期运行Spark作业,将此“增量数据”与当前版本的“快照表”(ORC格式)合并,以获得最新版本的上游快照 在此合并逻辑期间: 1) 我们将“增量数据”作为数据帧df1加载 2) 将当前“快照表”作为数据帧df2加载 3) 合并df1和df2,消除重复ID并获取行的最新版本(使用update_timestamp列) 该逻辑将“增量数据”和当前“快照表”的全部数据加载到Spark内存中,根据数据库
import io.delta.tables._
import org.apache.spark.sql.functions._
val updatesDF = ... // define the updates DataFrame[date, eventId, data]
DeltaTable.forPath(spark, "/data/events/")
.as("events")
.merge(
updatesDF.as("updates"),
"events.eventId = updates.eventId")
.whenMatched
.updateExpr(
Map("data" -> "updates.data"))
.whenNotMatched
.insertExpr(
Map(
"date" -> "updates.date",
"eventId" -> "updates.eventId",
"data" -> "updates.data"))
.execute()
在这里,“updatesDF”可以被认为是来自CDC源的“增量数据”
我的问题:
1) 合并/升级如何在内部工作?它是否将整个“updatedf”和“/data/events/”加载到Spark内存中
2) 如果没有,它是否应用了类似于ApacheHudi的增量更改
3) 在重复数据消除过程中,此upsert逻辑如何知道获取最新版本的记录?因为我没有看到任何指定“更新时间戳”列的设置
不,Spark不需要加载它需要更新到内存中的整个增量DF。
否则它就无法扩展。
它所采用的方法与Spark所做的其他工作非常相似——如果数据集足够大(或者您创建显式分区),则整个表将透明地拆分为多个分区。然后为每个分区分配一个单独的任务,该任务构成merge
作业。任务可以在不同的Spark执行器等上运行
2) If not, does it apply the delta changes something similar to Apache Hudi ?
我听说过阿帕奇胡迪,但还没看过。
在内部,Delta
看起来像版本化的拼花地板文件。
对表的更改存储为有序的原子单元,称为提交。
当你保存一个表时——看看它有什么文件——它会有文件
例如000000.json、000001.json等,它们中的每一个都将引用
子目录中底层拼花地板文件的操作集。例如
000000.json会说这个版本在时间上引用了拼花地板文件001
和002,以及000001.json会说这个版本在时间上不应该引用
这两个旧的拼花文件,仅使用拼花文件003
3) During deduplication how this upsert logic knows to take the latest version of a record?
Because I don't see any setting to specify the "update timestamp" column?
默认情况下,它引用最新的变更集。
时间戳是在Delta中实现此版本控制的内部机制。
从语法开始,您可以通过引用较旧的快照-请参阅
谢谢您的回复。您说过“整个表被透明地分割成多个分区”:这种透明分区是如何发生的?通过查看每个列的基数,选择一个具有适当基数的列进行分区?例如,如果您优化您的增量表,则输出拼花地板表的大小将为1Gb。自动压缩创建128Mb拼花文件。虽然Spark的并行化单元不是一个文件。对于拼花文件,它可以小到一个行组
。因此,您可以将任务小到一行组。如果您来自Hadoop,这些被称为splits
。在delta表上创建一个dataframe,看看您得到了多少默认分区。例如,如果你有一个delta表,下面有10个1Gb文件,你会得到10多个分区。据说delta lake有10亿条记录,分布在1000个分区中。在“updateDF”中,我们更新/添加了大约1000条记录。对于这些记录(特别是更新的行),这个合并逻辑如何识别所有2000个分区中要更新的分区?我想你没有加载所有的1000个分区吧?您是否在PKs和分区号之间保留某种内部映射/索引?
3) During deduplication how this upsert logic knows to take the latest version of a record?
Because I don't see any setting to specify the "update timestamp" column?