6

我们有以下场景:

  • 我们有一个现有的表,其中包含大约。150 亿条记录。它在创建时没有明确分区。
  • 我们正在创建带有分区的该表的副本,希望在某些类型的查询上能够更快地读取时间。
  • 我们的表在 Databricks Cloud 上,我们使用 Databricks Delta。
  • 我们通常按两列过滤,其中一列是实体的 ID(350k 不同的值),其中一列是事件发生的日期(到目前为止,有 31 个不同的值,但每天都在增加!)。

因此,在创建新表时,我们运行了如下查询:

CREATE TABLE the_new_table
USING DELTA
PARTITIONED BY (entity_id, date)
AS SELECT
  entity_id,
  another_id,
  from_unixtime(timestamp) AS timestamp,
  CAST(from_unixtime(timestamp) AS DATE) AS date
FROM the_old_table

此查询已运行 48 小时并且还在继续。我们知道它正在取得进展,因为我们在相关的 S3 前缀中找到了与第一个分区键对应的大约 250k 前缀,并且这些前缀中肯定存在一些大文件。

但是,我们很难准确地监控取得了多少进展,以及我们预计这需要多长时间。

在我们等待的时候,我们尝试了这样的查询:

CREATE TABLE a_test_table (
  entity_id STRING,
  another_id STRING,
  timestamp TIMESTAMP,
  date DATE
)
USING DELTA
PARTITIONED BY (date);

INSERT INTO a_test_table
SELECT
  entity_id,
  another_id,
  from_unixtime(timestamp) AS timestamp,
  CAST(from_unixtime(timestamp) AS DATE) AS date
FROM the_old_table
  WHERE CAST(from_unixtime(timestamp) AS DATE) = '2018-12-01'

请注意,这里新表模式的主要区别是我们仅在日期上进行分区,而不是在实体 ID 上进行分区。我们选择的日期几乎正好包含旧表数据的 4%,我想指出这一点,因为它远远超过 1/31。当然,由于我们选择的单个值恰好与我们分区的对象相同,因此我们实际上只写了一个分区,而可能是十万左右。

使用相同数量的工作节点创建此测试表需要 16 分钟,因此我们预计(基于此)创建 25 倍大的表只需要大约7 小时

这个答案似乎部分承认使用过多的分区会导致问题,但根本原因在过去几年中似乎发生了很大变化,因此我们试图了解当前的问题可能是什么;Databricks文档并不是特别有启发性。

根据发布的 S3 请求率指南,似乎增加分区(键前缀)的数量应该会提高性能。有害的分区似乎违反直觉。

总而言之:我们期望将数千条记录写入数千个分区中的每一个。看来,减少分区数量会大大减少写入表数据所需的时间。为什么这是真的?是否有关于应为特定大小的数据创建的分区数量的一般准则?

4

3 回答 3

2

您应该对数据进行分区,date因为听起来您会随着时间的推移不断添加数据。这是对时间序列数据进行分区的普遍接受的方法。这意味着您每天将写入一个日期分区,并且您之前的日期分区不会再次更新(一件好事)。

如果您的用例从中受益,您当然可以使用辅助分区键(即PARTITIONED BY (date, entity_id)

按日期分区将需要您始终按日期读取此数据,以获得最佳性能。如果这不是您的用例,那么您必须澄清您的问题。

多少个分区?

没有人可以回答您应该使用多少个分区,因为每个数据集(和处理集群)都是不同的。您要避免的是“数据倾斜”,即一名工作人员必须处理大量数据,而其他工作人员则处于空闲状态。例如,在您的情况下,如果一个clientid是您的数据集的 20%,就会发生这种情况。按日期分区必须假设每天的数据量大致相同,因此每个工作人员都保持同样忙碌。

我不具体了解 Databricks 如何写入磁盘,但在 Hadoop 上,我希望看到每个工作节点都写入它自己的文件部分,因此您的写入性能在这个级别上是并行的。

于 2019-03-01T16:18:52.213 回答
1

我根本不是数据砖专家,但希望这些项目符号可以提供帮助

分区数

无论如何,创建的分区和文件的数量都会影响你的工作的性能,尤其是使用 s3 作为数据存储,但是这个数量的文件应该可以通过下降大小的集群轻松处理

动态分区

通过 2 个键而不是一个键动态分区之间存在巨大差异,让我尝试更详细地解决这个问题。

当您对数据进行动态分区时,根据任务的数量和数据的大小,每个分区可能会创建大量小文件,这可能(并且可能会)影响需要使用此数据的下一个作业的性能,尤其是当您的数据以 ORC、parquet 或任何其他列格式存储时。请注意,这将只需要一个仅地图作业

前面解释的问题以不同的方式解决,最常见的是文件合并。为此,为了创建更大的文件,对数据进行了重新分区。因此,将需要对数据进行洗牌。

您的查询

对于您的第一个查询,分区数将为 350k*31(大约 11MM!),考虑到处理作业所需的洗牌量和任务量,这确实很大。

对于您的第二个查询(仅需 16 分钟),所需任务的数量和所需的改组数量要少得多。

分区的数量(洗牌/排序/任务调度/等)和你的作业执行时间没有线性关系,这就是为什么在这种情况下数学没有加起来的原因。

推荐

我想你已经明白了,你应该把你的 etl 作业分成 31 个不同的查询,这样可以优化执行时间

于 2019-03-01T15:49:46.417 回答
0

如果占用分区列,我的建议是

  • 识别所有列的基数并选择时间有限的列,因此排除标识符和日期列
  • 确定表的主要搜索,可能是日期或某些分类字段
  • 生成具有有限基数的子列,以加快搜索示例在日期的情况下可以将其分解为年、月、日等,或者在整数标识符的情况下,将它们分解为整数除法这些 ID 的% [1,2,3 ...]

正如我之前提到的,使用具有高基数的列进行分区会导致性能下降,因为会生成大量文件,这是最糟糕的工作情况。

建议在创建增量表时使用不超过 1 GB 的文件,建议占用“coalesce (1)”

如果需要执行更新或插入,指定最大的分区列数,以排除文件读取的不必要情况,这对于减少次数非常有效。

于 2020-09-11T23:42:56.003 回答