Scala 在大量分区上处理upsert不够快
问题 我们在ADLS Gen2上有一个Delta Lake设置,包括以下表格:Scala 在大量分区上处理upsert不够快,scala,apache-spark,databricks,delta-lake,azure-data-lake-gen2,Scala,Apache Spark,Databricks,Delta Lake,Azure Data Lake Gen2,问题 我们在ADLS Gen2上有一个Delta Lake设置,包括以下表格: brown.DeviceData:按到达日期进行分区(分区日期) silver.DeviceData:按事件日期和时间划分(Partition\u date和Partition\u hour) 我们将大量数据(每天超过6亿条记录)从活动中心摄取到brown.DeviceData(仅附加)。然后,我们以流式方式处理新文件,并使用delta MERGE命令(见下文)将其插入到silver.DeviceData中 到达
:按到达日期进行分区(brown.DeviceData
)分区日期
:按事件日期和时间划分(silver.DeviceData
和Partition\u date
)Partition\u hour
brown.DeviceData
(仅附加)。然后,我们以流式方式处理新文件,并使用delta MERGE命令(见下文)将其插入到silver.DeviceData
中
到达Brown表的数据可以包含来自任何silver分区的数据(例如,设备可以发送它在本地缓存的历史数据)。但是,任何一天到达的数据中,超过90%来自分区partitions\u Date IN(当前日期(),当前日期()-间隔1天,当前日期()+间隔1天)
。因此,为了升级数据,我们有以下两个spark任务:
- “Fast”:处理来自上述三个日期分区的数据。延迟在这里很重要,因此我们对这些数据进行优先级排序
- “Slow”:处理其余部分(除了这三个日期分区之外的任何部分)。延迟并不重要,但应该在“合理”的时间内(我认为不超过一周)
- 我们需要MERGE命令,因为某些上游服务可以重新处理历史数据,然后也应该更新silver表
- silver表的架构:
创建表silver.DeviceData(
DeviceID LONG NOT NULL,--发送数据的设备的ID
数据类型字符串不为NULL,--它发送的数据类型
Timestamp Timestamp NOT NULL,--数据点的时间戳
值DOUBLE NOT NULL,--设备发送的值
UpdatedTimestamp TIMESTAMP NOT NULL,--值到达时的时间戳
分区日期日期不为空,--=截止日期(时间戳)
分区\u Hour INT非空--=小时(时间戳)
)
使用DELTA
分区人(分区日期、分区小时)
位置“…”
- 我们的合并命令:
val silverTable=DeltaTable.forPath(spark,silverDeltaLakeDirectory)
val批处理=…//流式更新批处理
//我们要为分区修剪而插入的日期和时间
//从流式更新批处理中收集
val dates=“…”
val hours=“…”
val mergeCondition=s“”
silver.u日期单位($dates)
和silver.u小时($hours)
silver.Partition\u Date=batch.Partition\u Date
silver.Partition\u Hour=batch.Partition\u Hour
而silver.DeviceID=batch.DeviceID
和silver.Timestamp=batch.Timestamp
和silver.DataType=batch.DataType
"""
silverTable.别名(“silver”)
.merge(batch.alias(“batch”),mergeCondition)
//仅当事件较新时合并
.when匹配(“batch.UpdatedTimestamp>silver.UpdatedTimestamp”).updateAll
.when.insertAll
.执行
在Databricks上,有几种方法可以优化合并到
操作的性能:
- 对属于联接条件的列执行带ZOrder的优化。这可能取决于特定的DBR版本,因为旧版本(7.6 IIRC之前)使用的是real ZOrder算法,该算法适用于较少的列数,而DBR 7.6+默认使用Hilbert空间填充曲线
- 使用较小的文件大小-默认情况下,
创建1Gb的文件,这些文件需要重写。您可以使用OPTIMIZE
将文件大小设置为32Mb-64Mb范围,以便重写更少的数据spark.databricks.delta.optimize.maxFileSize
- 在表的分区上使用条件(您已经这样做了)
- 不要使用自动压缩,因为它不能执行ZOrder,而是使用ZOrder运行显式优化。详情见
- 调优,这样它将只索引条件和查询所需的列。它与合并部分相关,但可以稍微提高写入速度,因为不会为不用于查询的列收集统计信息
合并到中的优化-要观察哪些指标,等等
我不能100%确定您是否需要条件silver.Partition\u Date IN($dates)和silver.Partition\u Hour IN($hours)
,因为如果传入数据中没有特定的分区,您可能会读取比所需更多的数据,但需要查看执行计划。这说明了如何确保合并到中使用分区修剪。您使用的是Datatricks,还是您自己的delta lake?@AlexOtt其Datatricks“使用较小的文件大小”-我发现这非常有趣,现在每个(日期,小时)分区包含一个300MB的文件,据我所知,这意味着ZORDER或bloom filter指数