最佳实践

说明

详情请参考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删除它们。它针对文件删除进行了优化,通常比删除整个目录要快。