我们有以下方案:
- 我们有一个大约包含的现有表。150亿记录。它没有在创建上明确分区。
- 我们正在创建带有分区的该表的副本,希望在某些类型的查询上阅读时间更快。
- 我们的表位于Databricks云上,我们使用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上进行分区。我们选择的日期几乎包含旧表数据的四个%,我想指出的是,因为它超过1/31。当然,由于我们是通过单个值选择的,而单个值恰好是我们所划分的东西,所以我们实际上只编写一个分区,大约十万左右。
使用相同数量的工人节点,该测试表的创建花费了16分钟,因此我们希望(基于此)的创建表25x较大只能围绕 7小时
这个答案似乎部分地承认,使用过多的分区可能会导致问题,但是在过去的几年中,基本原因似乎发生了很大变化,因此我们试图了解当前的问题可能是什么。Databricks文档并没有特别启发。
基于S3的发布请求费率指南,似乎增加了分区数量(关键前缀)应改进性能。有害的分区似乎是违反直觉的。
总而言之:我们期望为成千上万的分区中的每个分区中的每个记录编写成千上万的记录。看来,减少分区的数量大大减少了编写表数据所需的时间。为什么这是真的?是否有有关应为特定大小的数据创建的分区数量的一般准则?
您应该通过date
将数据分配,因为它听起来就像您在时间顺序通过时不断添加数据一样。这是分区时间序列数据的普遍接受的方法。这意味着您每天都会写入一个日期分区,并且您以前的日期分区不会再次更新(这是一件好事)。
如果您的用例受益(即PARTITIONED BY (date, entity_id)
)
按日期进行分区将有必要始终在日期之前读取此数据,以获得最佳性能。如果这不是您的用例,那么您必须澄清您的问题。
多少个分区?
没有人可以回答您应该使用多少个分区,因为每个数据集(和处理群集)都不同。您要避免的是"数据倾斜",其中一个工人必须处理大量数据,而其他工人则闲着。例如,如果一个clientid
是您的数据集的20%,那将会发生。按日期进行分区必须假设每天的数据数量大致相同,因此每个工人都同样忙碌。
我不专门了解数据链曲奇如何将磁盘写入磁盘,但是在Hadoop上,我希望看到每个工人节点编写其自己的文件部分,因此您的写入性能在此级别平行于此。
我根本不是Databricks专家,但希望这些子弹可以帮助
分区数
无论如何,创建的分区和文件的数量都会影响您的作业绩效,尤其是将S3用作数据存储,但是该数量的文件数量应通过下降大小
来轻松处理。动态分区
通过您的2个键而不是一个键动态分区之间存在巨大差异,让我尝试在更多详细信息中解决这个问题。
当您动态分区数据时,根据任务的数量和数据大小,每个分区可以创建大量的小文件,这可能(并且可能)会影响需要使用此数据的下一个作业的执行,尤其是如果您的数据存储在兽人,镶木quet或任何其他柱状格式中时。请注意,这将仅需要仅映射作业。
以前解释的问题是以不同的方式解决的,是文件合并最常见的问题。为此,数据以创建更大的文件为目的进行重新分配。结果,需要进行数据改组。
您的查询
对于您的第一个查询,分区的数量将为350k*31(约11mm!),考虑到处理工作所需的改组和任务,这确实是很大的。
对于您的第二个查询(仅需16分钟),所需的任务和所需的改组的数量要小得多。
分区的数量(改组/排序/任务调度/etc)和工作执行时间没有线性关系,这就是为什么在这种情况下数学不添加的原因。
推荐
我认为您已经明白了,您应该在31个不同的查询中分配ETL作业,这将允许优化执行时间
我的建议如果占用分区的列为
- 确定所有列的基数,然后选择具有有限金额的列的基数,因此排除标识符和日期列
- 将主要搜索确定到表中,也许是日期或某些类别字段
- 生成具有有限基数的子列,以加快搜索示例的日期,可以将其分解为一年,月,每日等,或在整数标识符的情况下,将其分解为这些IDS%的整数[1,2,3 ...]
正如我之前提到的,使用具有高基数分区的列,将通过生成许多文件(最糟糕的工作案例)来导致性能差。
建议在创建三角洲表时使用不超过1 GB的文件,建议您占用" coalesce(1)""
如果您需要执行更新或插入,请指定最大数量的分区列来排除文件读取的案例,这对于减少时间非常有效。