【问题标题】:Query times out after 6 hours, how to optimize it?6小时后查询超时,如何优化?
【发布时间】:2020-05-27 17:51:45
【问题描述】:

我有两个表,shapessquares,我基于 GEOGRAHPY 列的交叉点加入。

shapes 表包含车辆的行驶路线:

shape_key        STRING            identifier for the shape
shape_lines      ARRAY<GEOGRAPHY>  consecutive line segments making up the shape
shape_geography  GEOGRAPHY         the union of all shape_lines
shape_length_km  FLOAT64           length of the shape in kilometers

Rows: 65k
Size: 718 MB

我们将shape_lines 分隔在ARRAY 中,因为形状有时会在自身上重复,我们希望将这些线段分开而不是deduplicating them

squares 表包含一个 1×1 平方千米的网格:

square_key        INT64      identifier of the grid square
square_geography  GEOGRAPHY  four-cornered polygon describing the grid square

Rows: 102k
Size: 15 MB

这些形状代表车辆的行驶路线。对于每种形状,我们在单独的表格中计算了有害物质的排放量。目的是计算每个方格的排放量,假设它们沿路线均匀分布。为此,我们需要知道路线形状的哪一部分与每个网格单元相交。

这是计算它的查询:

SELECT
  shape_key,
  square_key,
  SAFE_DIVIDE(
      (
        SELECT SUM(ST_LENGTH(ST_INTERSECTION(line, square_geography))) / 1000
        FROM UNNEST(shape_lines) AS line
      ),
      shape_length_km)
    AS square_portion
FROM
  shapes,
  squares
WHERE
  ST_INTERSECTS(shape_geography, square_geography)

遗憾的是,此查询在 6 小时后超时,而不是产生有用的结果。

在最坏的情况下,查询可以产生 66 亿行,但实际上不会发生这种情况。我估计每个形状通常与 50 个网格正方形相交,因此输出应该是大约 65k * 50 = 3.3M 行; BigQuery 不应该处理的任何事情。

我考虑过 BigQuery 执行的the geographic join optimizations

  • 空间连接是两个表的连接,在 WHERE 子句中使用谓词地理函数。

    检查。我什至将我的INNER JOIN 重写为上面显示的等效“逗号”连接。

  • 当您的地理数据被持久化时,空间连接的性能会更好。

    检查。 shape_geographysquare_geography 都直接来自现有表。

  • BigQuery 使用以下标准 SQL 谓词函数为 INNER JOIN 和 CROSS JOIN 运算符实现优化的空间 JOIN:[...]ST_Intersects

    检查。只需一个ST_Intersect 电话,没有其他条件。

  • 空间连接未优化:对于 LEFT、RIGHT 或 FULL OUTER 连接;在涉及 ANTI 连接的情况下;当空间谓词被否定时。

    检查。这些情况都不适用。

所以我认为 BigQuery 应该能够使用它使用的任何空间索引数据结构来优化此连接。

我也考虑过advice about cross joins

  • 避免产生输出多于输入的连接。

    这个查询肯定会产生比输入更多的输出;这是它的本质,无法避免。

  • 当需要CROSS JOIN 时,预先聚合您的数据。

    为避免与生成的输出多于输入的连接相关的性能问题:

    • 使用 GROUP BY 子句预先聚合数据。

    检查。我已经预先汇总了按形状分组的排放数据,因此shapes 表中的每个形状都是独一无二的。

    • 使用窗口函数。窗口函数通常比使用交叉连接更有效。如需更多信息,请参阅analytic functions

    我认为这个查询不能使用窗口函数。

我怀疑 BigQuery 根据输入行数分配资源,而不是根据中间表或输出的大小。这可以解释我所看到的病态行为。

我怎样才能使这个查询在合理的时间内运行?

【问题讨论】:

  • 您能提供表格样本吗?我想复制它
  • @rmesteves 谢谢。我已授予bigquery.dataViewer 访问allUsers 的权限,希望这就足够了。表的名称是open-transport-data.public.shapesopen-transport-data.public.squares
  • 逗号是CROSS JOIN - 这可能会破坏您的查询
  • @MartinWeitzmann 当然是交叉连接。就像我说的,这是野兽的本性。

标签: google-bigquery gis cartesian-product


【解决方案1】:

我认为squares 倒置了,导致几乎完整的地球多边形:

select st_area(square_geography), * from   `open-transport-data.public.squares`

打印像5.1E14 这样的结果 - 这是整个地球区域。所以任何一条线都与几乎所有的正方形相交。有关详细信息,请参阅 BigQuery 文档:https://cloud.google.com/bigquery/docs/gis-data#polygon_orientation

您可以通过运行 ST_GeogFromText(wkt, FALSE) 来反转它们 - 它选择较小的多边形,忽略多边形方向,这相当快:

SELECT
  shape_key,
  square_key,
  SAFE_DIVIDE(
      (
        SELECT SUM(ST_LENGTH(ST_INTERSECTION(line, square_geography))) / 1000
        FROM UNNEST(shape_lines) AS line
      ),
      shape_length_km)
    AS square_portion
FROM
  `open-transport-data.public.shapes`,
  (select 
       square_key, 
       st_geogfromtext(st_astext(square_geography), FALSE) as square_geography,
     from `open-transport-data.public.squares`) squares
WHERE
  ST_INTERSECTS(shape_geography, square_geography)

【讨论】:

  • 是的,就是这样,非常感谢!我现在要对 QGIS 说一些严厉的话,因为这些正方形来自的 CSV-with-WKT 导出似乎包含右手多边形,即使 WKT 应该使用左手规则。
【解决方案2】:

下面肯定不适合 cmets 格式,所以我必须将其发布为答案......

我对你的查询做了三处调整

  • 使用 JOIN ... ON 而不是 CROSS JOIN ... WHERE
  • 注释掉square_portion计算
  • 使用带有Allow Large Results选项的目标表

即使您预计输出只有 330 万行 - 实际上它大约是 6.6 B (6,591,549,944) 行 - 您可以在下面看到我的实验结果

请注意有关计费层级的警告 - 因此您最好使用预留(如果有)
显然,取消注释 square_portion 计算会增加 Slots 的使用 - 因此,您可能需要重新审视您的要求/期望

【讨论】:

  • 感谢您的彻底调查! 6.6B 的输出行意味着我们得到了一个完整的笛卡尔积……这不是我想要的。明天我会仔细看看数据。
猜你喜欢
  • 2013-08-31
  • 1970-01-01
  • 2020-09-07
  • 2023-03-09
  • 2023-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多