了解动态表刷新

动态表内容基于特定查询的结果。当动态表所基于的基础数据发生变更时,该表将会更新以反映这些变更。这些更新称为 刷新。此过程是自动化的,并且涉及到分析表的基础查询。

动态表刷新超时由 STATEMENT_TIMEOUT_IN_SECONDS 参数确定,定义账户或仓库在自动取消前的持续时间上限。

以下各部分更详细地解释了动态表刷新:

动态表刷新模式

动态表支持两种刷新模式:增量刷新和完全刷新。

  • 增量刷新: 此模式分析动态表的查询,并计算自上次刷新以来的变更。然后它将这些变更合并到表中。有关支持的查询的信息,请参阅 增量刷新中支持的查询

  • 完全刷新: 此模式涉及对动态表执行查询,并完全替换以前的物化结果。

您可以明确设置刷新模式,也可以将其设置为 AUTO。通过 AUTO,Snowflake 根据查询复杂性、支持的结构、运算符、函数和预期性能等因素,智能选择更具成本效益和时间效益的刷新模式。如果增量刷新不受支持或可能表现不佳,Snowflake 会自动选择完全刷新。有关更多信息,请参阅 增量刷新的限制

我们强烈建议在大多数使用情况下使用 AUTO,因为它允许 Snowflake 无需手动微调即可优化刷新行为。

创建动态表后,您可以监控表,以确定是使用增量刷新还是完全刷新来更新该表。

查看动态表刷新模式

动态表定义中的查询决定了其刷新模式。创建后,您可以查看动态表是使用增量刷新还是完全刷新。

使用具有 必要权限 的角色,您可以使用以下方法之一验证 刷新模式

执行 SHOW DYNAMIC TABLES 命令:

SHOW DYNAMIC TABLES;
Copy

输出 中:

  • text 列显示用户指定的刷新模式。

  • refresh_mode 列显示实际刷新模式。

  • refresh_mode_reason 显示选择实际刷新模式的原因。

增量刷新中支持的查询

下表描述了当前支持增量刷新的表达式、关键字和子句。有关不支持增量刷新的查询列表,请参阅 对增量刷新支持的限制

关键字/子句

对增量刷新的支持

WITH

公用表表达式 (CTE),在子查询中使用支持增量刷新的功能。

SELECT 中的表达式

包括使用确定性内置函数和 不可变 用户定义函数 的表达式。

FROM

源表、视图、Snowflake 管理的 Apache Iceberg™ 表和其他动态表。不支持 FROM 子句(例如 WHERE EXISTS)外部的子查询。

OVER

全部:doc:窗口函数</user-guide/functions-window-using>

WHERE/HAVING/QUALIFY

具有在 SELECT 中有效的相同表达式的筛选器。

JOIN(以及用于联接表的其他表达式)

支持增量刷新的联接类型包括内部联接、外部联接、交叉联接和横向展平(仅支持非静态 FLATTEN 表函数)。您可以在联接中指定任意数量的表,联接中所有表的更新都会反映在查询结果中。

不支持从横向展平连接中选择展平 SEQ 列进行增量刷新。

UNION ALL

受增量刷新模式支持。

GROUP BY

受增量刷新模式支持。

重要

如果查询使用增量刷新不支持的表达式,则自动刷新过程将改用完全刷新,这可能会导致额外的开销。要确定使用哪种刷新模式,请参阅 确定使用增量刷新还是完全刷新

当使用增量刷新的动态表使用 IMMUTABLE 用户定义的函数 (UDF) 时,替换该函数会导致刷新失败。增量刷新不支持 VOLATILE UDFs。

目前,增量刷新不支持横向连接。但是,您可以通过将刷新模式设置为 INCREMENTAL,来使用 FLATTEN() <label-lateral_flatten_example>` 的 :ref:`LATERAL。

例如:

CREATE TABLE persons
  AS
    SELECT column1 AS id, parse_json(column2) AS entity
    FROM values
      (12712555,
      '{ name:  { first: "John", last: "Smith"},
        contact: [
        { business:[
          { type: "phone", content:"555-1234" },
          { type: "email", content:"j.smith@example.com" } ] } ] }'),
      (98127771,
      '{ name:  { first: "Jane", last: "Doe"},
        contact: [
        { business:[
          { type: "phone", content:"555-1236" },
          { type: "email", content:"j.doe@example.com" } ] } ] }') v;

CREATE DYNAMIC TABLE example
  TARGET_LAG = DOWNSTREAM
  WAREHOUSE = mywh
  REFRESH_MODE = INCREMENTAL
  AS
    SELECT p.id, f.value, f.path
    FROM persons p,
    LATERAL FLATTEN(input => p.entity) f;
Copy

备注

不支持从横向展平连接中选择展平 SEQ 列进行增量刷新。

运算符如何增量刷新

下表概述了每个运算符的增量方式(即如何将其转换为生成更改而不是完整结果的新查询片段)、其性能,以及其他需要考虑的重要因素。

运算符

增量

注意事项

SELECT <scalar expressions>

通过将表达式应用于已更改的行实现递增。

表现良好,没有需要注意的特殊事项。

WHERE <scalar expressions>

通过计算每个已更改行上的谓词实现递增,仅包括谓词为 true 的行。

总体表现良好。成本随变化大小线性增长。

使用高度选择性的 WHERE 表达式刷新动态表可能需要仓库正常运行时间,即使生成的动态表没有变化。这是因为可能需要仓库来确定源中的哪些更改满足谓词。

FROM <base table>

通过扫描上次刷新以后添加到表或从表中移除的微分区来实现递增。

成本随添加或移除的微分区中的数据量线性增长。

建议:

  • 将每次刷新的更改量限制在源表的 5% 左右。

  • 注意会影响许多微分区的 DMLs。

<query> UNION ALL <query>

通过取两边更改并集实现递增。

表现良好,没有需要注意的特殊事项。

WITH <CTE list> <query>

通过计算每个常用表表达式的更改实现递增。

WITH 使复杂的查询更容易阅读,但注意不要使单个动态表的定义过于复杂。有关更多信息,请参阅 和 优化复杂动态表的增量刷新模式性能

标量汇总

标量汇总目前没有实现有效递增。当其输入发生变化时,会完全重新计算。

GROUP BY <keys>

通过重新计算每个更改的分组键的汇总来实现递增。

确保源数据按分组键聚类,并且更改占分组键的一小部分(大约 <5%)。

如果分组密钥包含复合表达式而不是基础列,增量刷新可能必须扫描大量数据。要减小这些扫描的大小,则在一个动态表中 具体化 表达式,然后在另一个动态表中对具体化列应用分组操作。

例如,以下面的复合语句为例:

CREATE DYNAMIC TABLE sums
  AS
    SELECT date_trunc(minute, ts), sum(c1) FROM table
    GROUP BY 1;
Copy

上述语句可以优化如下:

CREATE DYNAMIC TABLE intermediate
  AS
    SELECT date_trunc(minute, ts) ts_min, c1 FROM table;
Copy
CREATE DYNAMIC TABLE sums
  AS
    SELECT ts_min, sum(c1) FROM intermediate
    GROUP BY 1;
Copy

DISTINCT

相当于没有聚合函数的 GROUP BY ALL。

通常代表着实质性的优化机会。

常见做法是在整个查询中自由应用 DISTINCT,以避免意外引入重复项。在增量刷新中,DISTINCT 操作会周期性地消耗资源,因为每次刷新都必须检查重复项。

优化性能时,查找和删除冗余 DISTINCTs 可以轻松取胜。您可以通过消除更上游的重复项并谨慎考虑加入基数来实现。

<fn> OVER <window>

通过重新计算每个更改的分区键的窗口函数实现递增。

确保查询中有 PARTITION BY 子句,并且源数据按分区键聚类。还要确保更改占分区的一小部分(大约 <5%)。

<left> INNER JOIN <right>

通过将左侧的变更联接到右侧,然后将右侧的变更联接到左侧来实现递增。

如果联接的一侧很小,性能很可能不错。如果联接的一侧频繁变更,则通过联接键对另一侧进行聚类可能会提高性能。

<left> [{LEFT | RIGHT | FULL }] OUTER JOIN <right>

通过因子分解成一个内部联接 union-all-ed 与一到两个 NOT EXISTS 来计算非匹配的 NULLs,以此实现递增。然后递增此分解查询。

内部联接递增如下所示。通过检查在一端的变更键是否在另一端已经存在,来实现不存在值的递增。

建议:

  • 如果联接的一侧频繁变更,则通过联接键对另一侧进行聚类可能会提高性能。

  • 将频繁变更的表放在左侧。

  • 尽量减少 OUTER 对侧的变更。因此,对于 LEFT OUTER,尽量减少右侧的变更。

  • 对于 FULL 联接,位置非常重要。

完全刷新中支持的非确定性函数

动态表支持以下非确定性函数。请注意,这些函数仅支持完全刷新。有关增量刷新不支持的列表,请参阅 对增量刷新支持的限制

当动态表依赖于其他动态表时数据如何刷新

当动态表的滞后被设置为时间量度时,自动刷新流程会安排刷新,以更好地满足目标滞后时间。

备注

目标滞后不是保证,而是 Snowflake 尝试达到的目标。动态表中的数据会在尽可能不超出目标滞后的时间进行刷新。但是,由于仓库大小、数据大小、查询复杂性和类似因素等缘故,可能会超出目标滞后。

为了在 一个动态表依赖于另一个动态表 的情况下保持数据一致,该过程会在合适的时间刷新账户中的所有动态表。刷新频率较低的时间与刷新频率较高的时间相吻合。如果刷新时间过长,调度程序可能会跳过刷新,以尽量保持最新状态。但是,快照隔离仍会保留。

例如,假设动态表 A 的目标滞后为两分钟,并且查询目标滞后为一分钟的动态表 B。该过程可能会确定 A 应每 96 秒刷新一次,而 B 应每 48 秒刷新一次。因此,该过程可能会采用以下时间表:

具体时间点

刷新的动态表

2022-12-01 00:00:00

A、B

2022-12-01 00:00:48

B

2022-12-01 00:01:36

A、B

2022-12-01 00:02:24

B

这意味着在任何给定时间,当您查询一组相互依赖的动态表时,查询的是这些表中数据的相同“快照”。

请注意,动态表的目标滞后不能短于其所依赖的动态表的目标滞后。例如,假设:

  • 动态表 A 查询动态表 B 和 C。

  • 动态表 B 的目标滞后为五分钟。

  • 动态表 C 的目标滞后为一分钟。

这意味着 A 的目标滞后时间不得短于五分钟(即,不得短于 B 和 C 的滞后时间中较长的一个)。

如果将 A 的滞后设置为五分钟,该过程将设置具有以下目标的刷新时间表:

  • 经常刷新 C,使其滞后短于一分钟。

  • 经常同时刷新 A 和 B,使它们的滞后短于五分钟。

  • 确保 A 和 B 的刷新在时间上与 C 的刷新相吻合,以确保快照隔离。

重要

增量刷新模式下的动态表不能是完全刷新模式下动态表的下游。这是因为增量刷新模式不兼容上游完全刷新表每次刷新时发生的完整行变更。

语言: 中文