Apache spark 三角洲湖:upsert内部如何工作?

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内存中,根据数据库

在我们的数据管道中,我们从数据源接收CDC事件,并以AVRO格式将这些更改写入“增量数据”文件夹

然后定期运行Spark作业,将此“增量数据”与当前版本的“快照表”(ORC格式)合并,以获得最新版本的上游快照

在此合并逻辑期间:

1) 我们将“增量数据”作为数据帧df1加载

2) 将当前“快照表”作为数据帧df2加载

3) 合并df1和df2,消除重复ID并获取行的最新版本(使用update_timestamp列)

该逻辑将“增量数据”和当前“快照表”的全部数据加载到Spark内存中,根据数据库的不同,Spark内存可能相当大

我注意到,在Delta Lake中,类似的操作是使用以下代码完成的:

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?