详情请参考Databricks官网文章:最佳实践
本文介绍了使用Delta Lake时的最佳做法。
提供数据位置提示
如果您通常希望在查询谓词中使用一个列,并且该列具有较高的基数(即,大量不同的值),则使用Z-ORDER-BY。Delta-Lake根据列值自动布局文件中的数据,并在查询时使用布局信息跳过不相关的数据。
有关详细信息,请参见Z-Ordering(多维集群)。
选择正确的分区列
您可以按列对Delta表进行分区。最常用的分区列是date。遵循以下两个经验法则来确定要按哪个分区进行分区:
如果列的基数很高,请不要使用该列进行分区。例如,如果按列进行分区,userId并且可以有1M个不同的用户ID,则这是一个不好的分区策略。
每个分区中的数据量:如果您希望该分区中的数据至少为1 GB,则可以按列进行分区。
压缩文件
如果您不断将数据写入Delta表,随着时间的流逝,它将累积大量文件,尤其是如果您少量添加数据时。这可能会对表读取的效率产生不利影响,也可能影响文件系统的性能。理想情况下,应定期将大量小文件重写为少量大文件。这称为压缩。
您可以使用OPTIMIZE命令来压缩表。
替换表的内容或架构
有时您可能要替换Delta表。例如:
您发现表中的数据不正确,并且想要替换内容。
您想要重写整个表以进行不兼容的架构更改(删除列或更改列类型)。
虽然您可以删除Delta表的整个目录并在同一路径上创建新表,但不建议这样做,因为:
删除目录效率不高。包含非常大文件的目录可能要花费数小时甚至数天才能删除。
您会丢失已删除文件中的所有内容;如果删除错误的表,将很难恢复。
目录删除不是原子的。在删除表时,读取表的并发查询可能会失败或看到部分表。
如果不需要更改表架构,则可以从Delta表中删除数据并插入新数据,或者更新表以修复不正确的值。
如果要更改表架构,则可以atomic替换整个表。例如:
dataframe.write \
.format("delta") \
.mode("overwrite") \
.option("overwriteSchema", "true") \
.partitionBy(<your-partition-columns>) \
.saveAsTable("<your-table>") # Managed table
dataframe.write \
.format("delta") \
.mode("overwrite") \
.option("overwriteSchema", "true") \
.option("path", "<your-table-path>") \
.partitionBy(<your-partition-columns>) \
.saveAsTable("<your-table>") # External table
这种方法有很多好处:
覆盖表的速度要快得多,因为它不需要递归列出目录或删除任何文件。
该表的旧版本仍然存在。如果删除错误的表,则可以使用时间段轻松检索旧数据。
这是一个原子操作。删除表时,并发查询仍然可以读取表。
由于Delta Lake ACID事务保证,如果覆盖表失败,该表将处于其先前状态。
另外,如果要在覆盖表后删除旧文件以节省存储成本,则可以使用VACUUM删除它们。它针对文件删除进行了优化,通常比删除整个目录要快。