修复"资源超出"在BigQuery中,让它运行得更快

时间:2016-01-14 02:10:38

标签: sql google-bigquery

GDELT的Kalev Leetaru遇到了这个问题 - 在分析整整一个月时,以下查询将在BigQuery中运行,但是在整整一年中它不会运行。

SELECT Source, Target, count, RATIO_TO_REPORT(count) OVER() Weight
FROM (
  SELECT a.name Source, b.name Target, COUNT(*) AS COUNT
  FROM (FLATTEN(
      SELECT
        GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
      FROM [gdelt-bq:gdeltv2.gkg]
      WHERE DATE>20150100000000 and DATE<20151299999999, name)) a
  JOIN EACH (
    SELECT 
      GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
    FROM [gdelt-bq:gdeltv2.gkg]
    WHERE DATE>20150100000000 and DATE<20151299999999 ) b
  ON a.GKGRECORDID=b.GKGRECORDID
  WHERE a.name<b.name
    AND a.name != '0.000000#0.000000'
    AND b.name != '0.000000#0.000000'
  GROUP EACH BY 1, 2
  ORDER BY 3 DESC )
WHERE count > 50
LIMIT 500000

&#34;查询执行期间超出资源。&#34;

我们如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

首先是关于成本优化的说明:每列扫描的BigQuery费用,此查询将超过72GB。 GDELT gkg表将其整个故事存储在一个表中 - 我们可以通过创建年度表而不是单个表来优化成本。

现在,我们如何修复此查询以使其运行一整年? “在查询执行期间超出资源”通常来自不可扩展的功能。例如:

  • RATIO_TO_REPORT(COUNT) OVER()将无法扩展:OVER()函数在整个结果集上运行,允许我们计算总数以及每行总贡献的数量 - 但为此要运行,我们需要整个结果集适合一个VM。好消息是OVER()能够在分区数据时进行扩展,例如通过具有OVER(PARTITION BY月) - 然后我们只需要每个分区都适合VM。对于此查询,我们将删除此结果列,以简化。

  • ORDER BY不会缩放:要对结果进行排序,我们还需要将所有结果都放在一个VM上。这就是' - allow-large-results'不允许运行ORDER BY步骤的原因,因为每个VM将并行处理和输出结果。

在此查询中,我们有一种简单的方法来处理ORDER BY可伸缩性 - 我们将更早的过滤器“WHERE COUNT&gt; 50”移到流程中。我们将移动它并将其更改为HAVING,而不是对所有结果进行排序,并过滤那些COUNT> 50的结果,因此它在ORDER BY之前运行:

SELECT Source, Target, count
FROM (
  SELECT a.name Source, b.name Target, COUNT(*) AS COUNT
  FROM (FLATTEN(
      SELECT
        GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
      FROM [gdelt-bq:gdeltv2.gkg]
      WHERE DATE>20150100000000 and DATE<20151299999999,name)) a
  JOIN EACH (
    SELECT 
      GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
    FROM [gdelt-bq:gdeltv2.gkg]
    WHERE DATE>20150100000000 and DATE<20151299999999 ) b
  ON a.GKGRECORDID=b.GKGRECORDID
  WHERE a.name<b.name
    AND a.name != '0.000000#0.000000'
    AND b.name != '0.000000#0.000000'
  GROUP EACH BY 1, 2
  HAVING count>50
  ORDER BY 3 DESC )
LIMIT 500000

现在查询运行了整整一年的数据!

让我们看一下解释统计数据:

explanation stats for the first fixed query

我们可以看到188万行表被读了两次:第一个子查询产生了15亿行(给定“FLATTEN”),第二个子查询过滤掉了不在2015年的行(注意这个表开始存储数据在2015年初。)

第3阶段很有趣:加入两个子查询产生了30亿行!使用FILTER和AGGREGATE步骤将这些减少到5亿:

enter image description here

我们可以做得更好吗?

是的!让我们将2 WHERE a.name != '....'移到更早的“HAVING”:

SELECT Source, Target, count
FROM (
  SELECT a.name Source, b.name Target, COUNT(*) AS COUNT
  FROM (FLATTEN(
      SELECT
        GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
      FROM [gdelt-bq:gdeltv2.gkg]
      WHERE DATE>20150100000000 and DATE<20151299999999
      HAVING name != '0.000000#0.000000',name)) a
  JOIN EACH (
    SELECT 
      GKGRECORDID, CONCAT( STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)) ) AS name
    FROM [gdelt-bq:gdeltv2.gkg]
    WHERE DATE>20150100000000 and DATE<20151299999999 
    HAVING name != '0.000000#0.000000') b
  ON a.GKGRECORDID=b.GKGRECORDID
  WHERE a.name<b.name
  GROUP EACH BY 1, 2
  HAVING count>50
  ORDER BY 3 DESC )
LIMIT 500000

运行得更快!

让我们看一下解释统计数据:

enter image description here

请参阅?通过在加入之前将过滤移动到一个步骤,阶段3只需要经过10亿行,而不是30亿行。更快(即使对于BigQuery,你可以自己检查,在短时间内能够超过30亿行加入)。

这个查询是什么?

在这里查看美丽的结果:http://blog.gdeltproject.org/a-city-level-network-diagram-of-2015-in-one-line-of-sql/

enter image description here