【问题标题】:How to improve the performance of a SQL query in Snowflake, when joining multiple small tables?连接多个小表时,如何提高 Snowflake 中 SQL 查询的性能?
【发布时间】:2020-06-19 10:41:05
【问题描述】:

我目前正在使用 Snowflake 中的数据库,但我遇到了性能问题。

在我的查询中,我需要从包含其他表的外键的 MAIN_TABLE 中检索 100 行数据。 MAIN_TABLE 大小介于 50K-300K 行之间。

这个 MAIN_TABLE 包含 NUMBER(38,0) 类型的 ids(外键),并且这些字段/列的描述是通过简单的 INNER JOIN 从其他表中检索的。

辅助表(table_one、table_two...)非常小,所有的行数都少于 20 行,除了一个有大约 1500 行(仍然非常小)

问题是

如下所示,运行查询平均需要 1.7 秒才能完成。 如果我删除所有 DESC 字段(使用 INNER JOIN 检索)和所有 INNER JOIN,则查询平均需要 300 毫秒才能完成。

我的同行实施的当前解决方案

他们已经缓存了我们应用程序中所有辅助表的信息(用Java制作,但这无关紧要)。对于大约 80% 的情况(请参阅下面的查询),我们不需要按 DESCription 过滤,这是可行的,但在其他 20% 的情况下,我们仍然需要执行 INNER JOIN。

缩小解决方案

不是一个糟糕的解决方案,但它有两个问题:

  1. (还不错)它使我们的代码库膨胀,基本上是用 Java 制作的“JOIN”
  2. 在我们需要按 DESCriptions 过滤的 20% 的时间里,我们仍然需要执行 INNER JOIN,因此我们仍然存在性能问题。

想到的一种可能性是在 SF 中缓存这些表,但我还没有找到一种直接的方法来做到这一点。也许有一种优化查询的方法,但我还不明白 Snowflake 内部是如何工作的,据我所知它不使用索引,至少不像其他平台那样。

那么,有没有办法针对 100% 的情况优化 SF 中的查询?

SELECT 
  main_table.ONE_ID, 
  main_table.TWO_ID, 
  main_table.THREE_ID, 
  main_table.FOUR_ID, 
  main_table.FIVE_ID, 
  main_table.SIX_ID, 
  main_table.SEVEN_ID, 
  field_one.ONE_DESC, 
  field_two.TWO_DESC, 
  field_three.THREE_DESC,
  field_four.FOUR_DESC, 
  field_five.FIVE_DESC, 
  field_six.SIX_DESC, 
  field_seven.SEVEN_DESC

FROM 
  SOME_DATABASE.MAIN_TABLE AS main_table 

  INNER JOIN SOME_DATABASE.TABLE_ONE    AS table_one    ON main_table.field_one_id =    table_one    .ONE_ID
  INNER JOIN SOME_DATABASE.TABLE_TWO    AS table_two    ON main_table.field_two_id =    table_two    .TWO_ID
  INNER JOIN SOME_DATABASE.TABLE_THREE  AS table_tree   ON main_table.field_tree_id =   table_tree   .THREE_ID
  INNER JOIN SOME_DATABASE.TABLE_FOUR   AS table_four   ON main_table.field_four_id =   table_four   .FOUR_ID
  INNER JOIN SOME_DATABASE.TABLE_FIVE   AS table_five   ON main_table.field_five_id =   table_five   .FIVE_ID
  INNER JOIN SOME_DATABASE.TABLE_SIX    AS table_six    ON main_table.field_six_id =    table_six    .SIX_ID
  INNER JOIN SOME_DATABASE.TABLE_SEVEN  AS table_seven  ON main_table.field_seven_id =  table_seven  .SEVEN_ID

WHERE 
  main_table.ONE_ID IN (25, 26) 
  AND main_table.TWO_ID IN (10, 12) 
  AND main_table.THREE_ID IN (1, 2, 3) 
  AND main_table.FOUR_ID IN (2, 3) 
  AND main_table.FIVE_ID IN (3) 
  AND main_table.SEVEN_ID IN (1) 

  -- The following WHERE clauses are present in about 20% of the queries
  AND table_one.ONE_DESC, 
  AND table_two.TWO_DESC, 
  AND table_three.THREE_DESC,

ORDER BY 
  main_table.ONE_ID, 
  main_table.TWO_ID, 
  main_table.THREE_ID

LIMIT 
  100 OFFSET 0

小更新:

我一直在尝试使用 WITH 子句,同时包装 id 和描述,但似乎没有改进

【问题讨论】:

  • 当您运行这些测试时,您是否看到正在使用大量缓存?查询配置文件中每个表的修剪是什么?您是否尝试过使用事实中的 DESC 字段创建一个新表并进行比较?
  • 您是否检查过查询配置文件以了解最耗时的内容?您可能使用 CTE 走在正确的轨道上,应该首先将配置文件中具有长进程的小表/联接取出到 CTE 的它们自己的部分中。

标签: sql snowflake-cloud-data-platform


【解决方案1】:

我只是好奇,如果您在子查询中对主表进行过滤来表述查询是否会有所不同:

FROM (SELECT *
      FROM main_table
      WHERE main_table.ONE_ID IN (25, 26) AND
            main_table.TWO_ID IN (10, 12) AND
            main_table.THREE_ID IN (1, 2, 3) AND
            main_table.FOUR_ID IN (2, 3) AND
            main_table.FIVE_ID IN (3) AND
            main_table.SEVEN_ID IN (1) 
     ) main_table JOIN
    . . . 

巨大的可扩展性的缺点之一是使用的方法可能会增加较小查询的开销。也就是说,如果您将表大小乘以 1000,查询可能仍然只需要几秒钟 - 但这无助于您在较小的数据上获得更快的时间。

【讨论】:

  • 我试过了,但它似乎并没有提高性能,我得到的结果与我作为示例发布的查询非常相似
  • @DavidAlberici 。 ..尝试一次添加一个连接。查看一个特定的连接是否存在问题,或者速度是否逐渐变慢。
【解决方案2】:

您是否偶然尝试过重新设计您的字典表?就像你有一个:

  • DT1、DT2、DT3 带键、值各

你可以尝试把它变成

  • DT 字典 ID、键、值

这一次,您将拥有 1 个稍大且更复杂的连接条件,而不是 5 个小表,因为您必须为每个表添加字典 ID,但仍然可以在单个数据块而不是 5 个块

【讨论】:

    【解决方案3】:

    我的第一个想法是,snowflake 的设计目的不是为了快速询问这样的小问题,而是为了回答大问题。因此对我来说 1.xs 是很好的响应时间。

    但鉴于您希望它更快..

    我会首先检查慢查询的编译时间是否与快速查询相同。因为如果您的表是碎片化的,则需要读取更多元数据。

    接下来,我将查看执行的概要文件,看看时间花在了哪里。过去我们发现了类似的东西

    SELECT columnA, columnB FROM table where ID == 1
    UNION ALL 
    SELECT columnA, columnB FROM table where ID == 2 
    

    要快于

    SELECT columnA, columnB FROM table where ID in (1,2)
    

    我们发现 Gordon 在从表中进行 SELECT 时显式放置 WHERE 子句的巨大 SQL 语句有助于优化器混淆的地方。

    但另一方面可能只是额外的数据需要更长的时间才能传输到您的客户端(如果您使用客户端时间作为判断),或者如果您查看 WebUI 中的性能,您读取的数据越多,时间越长需要运行查询。所以即使你建一个新表

    CREATE TABLE testo AS
    SELECT 
      main_table.ONE_ID, 
      main_table.TWO_ID, 
      main_table.THREE_ID, 
      main_table.FOUR_ID, 
      main_table.FIVE_ID, 
      main_table.SIX_ID, 
      main_table.SEVEN_ID, 
      table_one.ONE_DESC, 
      table_two.TWO_DESC, 
      table_three.THREE_DESC,
      table_four.FOUR_DESC, 
      table_five.FIVE_DESC, 
      table_six.SIX_DESC, 
      table_seven.SEVEN_DESC
    FROM 
        SOME_DATABASE.MAIN_TABLE AS main_table 
    INNER JOIN SOME_DATABASE.TABLE_ONE AS table_one    
        ON main_table.field_one_id = table_one.ONE_ID
    INNER JOIN SOME_DATABASE.TABLE_TWO AS table_two
        ON main_table.field_two_id = table_two.TWO_ID
    INNER JOIN SOME_DATABASE.TABLE_THREE AS table_tree
        ON main_table.field_tree_id = table_tree.THREE_ID
    INNER JOIN SOME_DATABASE.TABLE_FOUR AS table_four
        ON main_table.field_four_id = table_four.FOUR_ID
    INNER JOIN SOME_DATABASE.TABLE_FIVE AS table_five
        ON main_table.field_five_id = table_five.FIVE_ID
    INNER JOIN SOME_DATABASE.TABLE_SIX AS table_six
        ON main_table.field_six_id = table_six.SIX_ID
    INNER JOIN SOME_DATABASE.TABLE_SEVEN AS table_seven  
        ON main_table.field_seven_id = table_seven.SEVEN_ID
    

    然后做

        ONE_ID, 
        TWO_ID, 
        THREE_ID, 
        FOUR_ID, 
        FIVE_ID, 
        SIX_ID, 
        SEVEN_ID, 
        ONE_DESC, 
        TWO_DESC, 
        THREE_DESC,
        FOUR_DESC, 
        FIVE_DESC, 
        SIX_DESC, 
        SEVEN_DESC
    FROM testo
    WHERE 
        ONE_ID IN (25, 26) 
        AND TWO_ID IN (10, 12) 
        AND THREE_ID IN (1, 2, 3) 
        AND FOUR_ID IN (2, 3) 
        AND FIVE_ID IN (3) 
        AND SEVEN_ID IN (1)   
    

    将需要更长的时间:

        ONE_ID, 
        TWO_ID, 
        THREE_ID, 
        FOUR_ID, 
        FIVE_ID, 
        SIX_ID, 
        SEVEN_ID
    FROM testo
    WHERE 
        ONE_ID IN (25, 26) 
        AND TWO_ID IN (10, 12) 
        AND THREE_ID IN (1, 2, 3) 
        AND FOUR_ID IN (2, 3) 
        AND FIVE_ID IN (3) 
        AND SEVEN_ID IN (1)   
    

    最后,我知道这是示例代码,但是将表从 main_table 别名为 main_table 并没有什么意义,因为别名/名称已经存在。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-03
      • 2016-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多