BigQuery 连接对于小尺寸表来说太慢了
Posted
技术标签:
【中文标题】BigQuery 连接对于小尺寸表来说太慢了【英文标题】:BigQuery join too slow for a table of small size 【发布时间】:2018-06-05 06:41:27 【问题描述】:我有一张包含以下详细信息的表格: - 表格大小 39.6 MB - 行数 691,562 - 2 列:contact_guid STRING、program_completed STRING - 第 1 列数据类型类似于 uuid。大约 30 个字符长度 - 第 2 列数据类型是长度约为 50 个字符的字符串
我正在尝试这个查询:
#standardSQL
SELECT
cp1.contact_guid AS p1,
cp2.contact_guid AS p2,
COUNT(*) AS cnt
FROM
`data.contact_pairs_program_together` cp1
JOIN
`data.contact_pairs_program_together` cp2
ON
cp1.program_completed=cp2.program_completed
WHERE
cp1.contact_guid < cp2.contact_guid
GROUP BY
cp1.contact_guid,
cp2.contact_guid having cnt >1 order by cnt desc
执行时间:1200 秒
我知道我正在进行自加入,并且在最佳实践中提到了避免自加入。
我的问题:
-
我觉得这个以 mb 为单位的表大小对于 BigQuery 来说太小了,为什么要花这么多时间?就行数和字节大小而言,小表在连接上下文中对 BigQuery 意味着什么?
行数是否太大? 700k ^ 2 在连接期间是 10^11 行。联接的实际行数是多少?
我确实检查了有关连接的文档,但没有找到关于连接的表有多大以及它可以运行多长时间的信息。我们如何估算粗略的执行时间?
执行细节:
【问题讨论】:
我认为 BigQuery 不允许您添加索引,这可能对您的查询有帮助。您的表几乎有一百万条记录;运行时间并不让我感到惊讶。 1) 您能否发布运行查询后“详细信息”选项卡显示的执行时间线? 2) 你可以尝试加入 ... ON .. AND,而不是 JOIN ... ON ... WHERE? @FelipeHoffa 我添加了执行时间表。将更新您建议的替代方案。 @TimBiegeleisen 我知道它几乎有 100 万条记录,但表太小了。只是想知道必须在没有索引的 Bigquery 中使用连接,通常我们会有包含这么多行的表。使查询工作或至少获得有关执行时间的粗略估计的方法是什么。我完全糊涂了。 我能提出的唯一建议是看看您是否可以使用分析函数重新表述您的查询。这可能会给您带来性能提升。 【参考方案1】:如您提供的屏幕截图所示 - 您正在处理爆炸连接。
在这种情况下,第 3 步需要 130 万行,并设法生成 4.59 亿行。步骤 04 到 0B 处理重新分区和重新洗牌所有额外数据 - 因为查询没有提供足够的资源来处理这些行数:它从 1 个并行输入扩展到 10,000 个!
您有两个选择:要么避免爆炸连接,要么假设爆炸连接需要很长时间才能运行。但正如问题中所解释的 - 你已经知道了!
如果您在一个操作中生成所有额外的行(执行连接、实现),然后运行另一个查询来处理 4.59 亿行,会怎么样?由于上述原因,第一个查询会很慢,但第二个查询会运行得很快,因为 BigQuery 会提供足够的资源来处理大量数据。
【讨论】:
我认为用例需要加入。但是知道分析函数可以做类似的事情。关于加入、具体化和另一个查询,我想我明白了。最初的数据只有 40mb 左右,所以 bigquery 提供了一些资源,而如果连接被实现,那么连接表将是 43GB 左右,现在如果我们运行查询,bigquery 将提供更多资源并且应该更快。但我将收取 43GB 扫描而不是 40MB 的费用。这就是权衡。我对吗?在另一个答案中,使用了分析函数。你能告诉在这种情况下这是否比加入更好吗? 我尝试实现连接并且它工作得更快。没有具体化:::查询完成(经过 1337.4 秒,处理了 39.6 MB)。使用 Materialized join::: join cmd: Query complete (303.9s elapsed, 39.6 MB processed) 。按 cmd 分组:查询完成(经过 31.6 秒,已处理 24.0 GB)。它是 24GB 而不是 43GB,因为屏幕截图结果在查询中有一个字符串 agg。所以结论是物化连接工作得更快,但我们需要为物化连接表的额外字节付费。谢谢你,费利佩。希望我理解正确。请纠正我,以防我弄错了。【参考方案2】:同意以下建议
看看您是否可以使用分析函数(Tim)重新表述您的查询
使用分析函数会是一个更好的主意(Elliott)
下面是我的做法
#standardSQL
SELECT
p1, p2, COUNT(1) AS cnt
FROM (
SELECT
contact_guid AS p1,
ARRAY_AGG(contact_guid) OVER(my_win) guids
FROM `data.contact_pairs_program_together`
WINDOW my_win AS (
PARTITION BY program_completed
ORDER BY contact_guid DESC
RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
)
), UNNEST(guids) p2
GROUP BY p1, p2
HAVING cnt > 1
ORDER BY cnt DESC
如果有帮助,请尝试告诉我们
【讨论】:
尝试执行但遇到以下错误。错误:在基于 RANGE 的窗口中,ORDER BY 键必须是数字,具有 OFFSET PRECEDING 或 OFFSET FOLLOWING 边界,但在 [11:5] 处具有 STRING 类型 没错,我在这里想到了数字。行。因此,如果它是一个字符串并称为 GUID - 我会假设它是唯一的,因此您可以将RANGE
替换为 ROWS
- 尝试让我知道这是否有帮助
已执行,“查询完成(经过 1197.7 秒,已处理 39.6 MB)”。类似的时间。我的问题是,如果我们连接两个表,比如 1m 行,是否可以理解这将花费这么多时间并且是合理的。为什么我问是我想知道什么标准会使查询运行得更快(减少 1/3 或 1/2)。分析函数是这类查询的正确选择吗?感谢您的快速回复:)。
一般来说 - 如果可能的话,使用分析功能比加入更可取
自联接查询的输出(1,986,138 行)和使用此分析版本的输出(2,000,599 行)不同。任何想法或指针为什么输出中的行数会有这种差异?以上是关于BigQuery 连接对于小尺寸表来说太慢了的主要内容,如果未能解决你的问题,请参考以下文章
VS Code - 重命名符号对于 Python 来说太慢了