了解和使用 Time Travel

Snowflake Time Travel 允许在定义的时间段内的任何时间点访问历史数据(即已更改或删除的数据)。它是执行以下任务的强大工具:

  • 恢复可能被意外或有意删除的数据相关对象(表、架构和数据库)。

  • 复制和备份过去关键点的数据。

  • 分析指定时间段内的数据使用/操作。

本主题内容:

Time Travel 简介

持续数据保护生命周期中的 Time Travel

使用 Time Travel,您可以在定义的时间段内执行以下操作:

  • 查询过去已更新或删除的数据。

  • 创建过去特定时间点或之前的整个表、架构和数据库的克隆。

  • 恢复已删除的表、架构和数据库。

备注

在表或非物化视图中查询历史数据时,使用当前表或视图架构。有关更多信息,请参阅 AT | BEFORE 的 使用说明

定义的时间段过后,数据将移动到 Snowflake 故障安全 中,并且无法再执行这些操作。

备注

长时间运行的 Time Travel 查询将延迟将账户中的任何数据和对象(表、架构和数据库)移动到故障安全,直到查询完成。

Time Travel SQL 扩展

为了支持 Time Travel,已经实现了以下 SQL 扩展:

  • AT | BEFORE 子句,可以在 SELECT 语句和 CREATE ...CLONE 命令(紧随对象名称之后)。该子句使用以下参数之一来精确定位您希望访问的确切历史数据:

    • TIMESTAMP

    • OFFSET (与现在时间的时差,以秒为单位)

    • STATEMENT (语句的标识符,例如查询 ID)

  • UNDROP 命令,用于表、架构和数据库。

    Time Travel SQL 扩展

数据保留期

Snowflake Time Travel 的一个关键组成部分是数据保留期。

当表中的数据被修改时(包括删除数据或删除包含数据的对象),Snowflake 会保留更新前的数据状态。数据保留期指定了此历史数据的保留天数,因此可以对数据执行 Time Travel 操作(SELECT、CREATE ... CLONE、UNDROP)。

标准保留期为 1 天(24 小时),并自动为所有 Snowflake 账户启用:

  • 对于 Snowflake Standard Edition,可以在账户和对象级别(即数据库、架构和表)将保留期设置为 0(或取消设置为默认值 1 天)。

  • 对于 Snowflake Enterprise Edition(及更高版本):

    • 对于暂时性数据库、架构和表,可以将保留期设置为 0(或取消设置为默认值 1 天)。临时表也是如此。

    • 对于永久数据库、架构和表,保留期可以设置为 0 到 90 天之间的任何值。

备注

对象的保留期为 0 天会有效地禁用对象的 Time Travel。

当对象的保留期结束时,历史数据将移动到 Snowflake 故障安全 中:

  • 历史数据不可再用于查询。

  • 无法再克隆过去的对象。

  • 无法再恢复过去删除的对象。

要指定 Time Travel 的数据保留期,请执行以下操作:

  • 具有 ACCOUNTADMIN 角色的用户可以使用 DATA_RETENTION_TIME_IN_DAYS 对象参数为您的账户设置默认保留期。

  • 在创建数据库、架构和单个表时,可以使用相同的参数显式替换默认值。

  • 可以随时更改数据库、架构或表的数据保留期。

  • 具有 ACCOUNTADMIN 角色的用户可以设置 MIN_DATA_RETENTION_TIME_IN_DAYS 账户参数,以设置账户的最短保留期。此参数不会更改或替换 DATA_RETENTION_TIME_IN_DAYS 参数值。但是,可能会更改有效的数据保留时间。在账户级别设置此参数时,对象的有效最短数据保留期由 MAX(DATA_RETENTION_TIME_IN_DAYS、MIN_DATA_RETENTION_TIME_IN_DAYS)确定。

启用和禁用 Time Travel

启用 Time Travel 不需要任何任务。系统会自动启用标准的 1 天保留期。

但是,您可能希望升级到 Snowflake Enterprise Edition,以便为数据库、架构和表配置长达 90 天的更长的数据保留期。请注意,延长数据保留期需要额外的存储空间,这将反映在您的每月存储费用中。有关存储费用的详细信息,请参阅 Time Travel 和故障安全的存储成本

无法为账户禁用 Time Travel。具有 ACCOUNTADMIN 角色的用户可以在账户级别将 DATA_RETENTION_TIME_IN_DAYS 设置为 0,这意味着在该账户中创建的所有数据库(以及随后的所有架构和表)默认情况下都没有保留期;但是,任何数据库、架构或表都可以随时替换此默认值。

具有 ACCOUNTADMIN 角色的用户还可以在账户级别设置 MIN_DATA_RETENTION_TIME_IN_DAYS。此参数设置强制执行数据库、架构和表的最短数据保留期。设置 MIN_DATA_RETENTION_TIME_IN_DAYS 不会改变或替换 DATA_RETENTION_TIME_IN_DAYS 参数值。但是,可能会更改对象的有效数据保留期。在账户级别设置 MIN_DATA_RETENTION_TIME_IN_DAYS 时,对象的数据保留期由 MAX(DATA_RETENTION_TIME_IN_DAYS、MIN_DATA_RETENTION_TIME_IN_DAYS)决定。

通过为对象指定值为 0 的 DATA_RETENTION_TIME_IN_DAYS,可以为单个数据库、架构和表禁用 Time Travel。但是,如果 DATA_RETENTION_TIME_IN_DAYS 的值设置为 0,并且 MIN_DATA_RETENTION_TIME_IN_DAYS 在账户级别设置且大于 0,则较高的值设置优先。

注意

在将任何对象的 DATA_RETENTION_TIME_IN_DAYS 设置为 0 之前,请考虑是否希望对该对象禁用 Time Travel,特别是在该对象被删除却想要还原时。删除没有保留期的对象时,您将无法恢复该对象。

作为一般规则,我们建议将任何给定对象的值保持为(至少)1 天。

如果 Time Travel 保留期设置为 0,则任何修改或删除的数据都将由后台进程移动到故障安全(对于永久表)或删除(对于瞬态表)。这可能需要很短的时间就会完成。在此期间,表存储指标中的 TIME_TRAVEL_BYTES 可能包含非零值,即使 Time Travel 保留期为 0 天。

指定对象的数据保留期

默认情况下,最长保留期为 1 天(即一个 24 小时期限)。使用 Snowflake Enterprise Edition(及更高版本)时,您的账户的默认值可以设置为最多 90 天的任何值:

  • 创建表、架构或数据库时,可以使用命令中的 DATA_RETENTION_TIME_IN_DAYS 参数替换账户默认值。

  • 如果为数据库或架构指定了保留期,则默认情况下,对于在数据库/架构中创建的所有对象,将继承该保留期。

可以使用 MIN_DATA_RETENTION_TIME_IN_DAYS 参数在账户上设置最短保留期。如果在账户级别设置此参数,则对象的数据保留期由 MAX(DATA_RETENTION_TIME_IN_DAYS、MIN_DATA_RETENTION_TIME_IN_DAYS)确定。

更改对象的数据保留期

如果更改表的数据保留期,则新的保留期会影响所有处于活动状态的数据,以及当前在 Time Travel 中的任何数据。影响取决于您是增加还是减少期限:

增加保留期:

使当前在 Time Travel 中的数据保留更长的时间段。

例如,如果您有一个保留期为 10 天的表,并将保留期增加到 20 天,则本应在 10 天后删除的数据现在将再保留 10 天,然后再进入故障安全。

请注意,这不适用于超过 10 天且已移至故障安全的任何数据。

减少保留期:

减少数据在 Time Travel 中的保留时间:

  • 对于在保留期缩短后修改的活动数据,将适用新的较短期限。

  • 对于当前在 Time Travel 中的数据:

    • 如果数据仍在新的较短期限内,则它仍保留在 Time Travel 中。

    • 如果数据超出新期限,则会进入故障安全。

例如,如果您有一个保留期为 10 天的表,并且将该保留期缩短为 1 天,则第 2 天到第 10 天的数据将移至故障安全,仅第 1 天的数据可通过 Time Travel 访问。

但是,将数据从 Time Travel 移动到故障安全的过程是由后台进程执行的,因此更改不会立即可见。Snowflake 保证数据将被移动,但没有指定该进程何时完成;在后台进程完成之前,数据仍可通过 Time Travel 访问。

备注

如果更改数据库或架构的数据保留期,则更改仅影响数据库或架构中包含的活动对象。任何已删除的对象(例如,表)都不受影响。

例如,如果您有一个保留期为 90 天的架构 s1,而表 t1 位于架构 s1 中,则表 t1 将继承 90 天的保留期。如果删除表 s1.t1t1 会在 Time Travel 中保留 90 天。稍后,如果将架构的数据保留期更改为 1 天,则被删除的表 t1 的保留期保持不变。表 t1 仍将在 Time Travel 中保留 90 天。

若要更改已删除对象的保留期,必须取消删除该对象,然后更改其保留期。

要更改对象的保留期,请使用相应的 ALTER <object> 命令。例如,要更改表的保留期,请执行以下操作:

CREATE TABLE mytable(col1 NUMBER, col2 DATE) DATA_RETENTION_TIME_IN_DAYS=90;

ALTER TABLE mytable SET DATA_RETENTION_TIME_IN_DAYS=30;
Copy

注意

更改账户或单个对象的保留期会更改未明确设置保留期的所有较低级别对象的值。例如:

  • 如果在账户级别更改保留期,则所有没有显式保留期的数据库、架构和表都将自动继承新的保留期。

  • 如果在架构级别更改保留期,则架构中没有显式保留期的所有表都将继承新的保留期。

在更改您的账户或账户中任何对象的保留期时,请记住这一点,因为更改可能会产生您没有预料到或打算的 Time Travel 后果。特别是,我们 建议在账户级别将保留期更改为 0。

删除的容器和对象保留继承

目前,删除数据库时,如果将子架构或表的数据保留期显式设置为不同于数据库的保留期,则不遵守该保留期。子架构或表的保留时间与数据库相同。

同样,删除架构时,如果子表的数据保留期被显式设置为不同于架构的保留期,则不遵守该保留期。子表的保留时间与架构相同。

要遵守这些子对象(架构或表)的数据保留期,请在删除数据库或架构 之前 显式删除它们。

查询历史数据

对表执行任何 DML 操作时,Snowflake 会在定义的时间段内保留以前版本的表数据。这样就可以使用 AT | BEFORE 子句查询早期版本的数据。

此子句支持在保留期内在表历史记录中的指定点处或紧接之前查询数据。指定点可以是基于时间的(例如,时间戳或与当前时间的时间偏移量),也可以是完整语句的 ID(例如 SELECT 或 INSERT)。

例如:

  • 以下查询从表中选择截至指定 时间戳 所表示的日期和时间的历史数据:

    SELECT * FROM my_table AT(TIMESTAMP => 'Fri, 01 May 2015 16:20:00 -0700'::timestamp_tz);
    
    Copy
  • 以下查询从截至 5 分钟前的表中选择历史数据:

    SELECT * FROM my_table AT(OFFSET => -60*5);
    
    Copy
  • 以下查询从表中选择历史数据,但不包括指定语句所做的任何更改:

    SELECT * FROM my_table BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726');
    
    Copy

备注

如果 AT | BEFORE 子句中指定的 TIMESTAMP、OFFSET 或 STATEMENT 超出了表的数据保留期,查询将失败并返回错误。

克隆历史对象

除了查询之外,AT | BEFORE 子句还可以与表、架构或数据库的 CREATE 命令中的 CLONE 关键字结合使用,以便在对象历史记录中的指定点创建对象的逻辑副本。

例如:

  • 以下 CREATE TABLE 语句创建自指定时间戳表示的日期和时间起的表的克隆:

    CREATE TABLE restored_table CLONE my_table
      AT(TIMESTAMP => 'Sat, 09 May 2015 01:01:00 +0300'::timestamp_tz);
    
    Copy
  • 以下 CREATE SCHEMA 语句创建架构及其所有对象的克隆,因为它们在当前时间前 1 小时已存在:

    CREATE SCHEMA restored_schema CLONE my_schema AT(OFFSET => -3600);
    
    Copy
  • 以下 CREATE DATABASE 语句创建数据库及其所有对象的克隆,因为它们在完成指定语句之前就已存在:

    CREATE DATABASE restored_db CLONE my_db
      BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726');
    
    Copy

备注

数据库或架构的克隆操作失败:

  • 如果指定的 Time Travel 时间超出了实体的 任何当前子项 (例如,表)的保留时间。

    作为已从 Time Travel 中清除的子对象的解决方法,请使用 IGNORE TABLES WITH INSUFFICIENT DATA RETENTION 参数,该参数属于 CREATE <object> ...CLONE 命令。有关更多信息,请参阅 子对象和数据保留时间

  • 如果指定的 Time Travel 时间在创建对象的时间点或之前。

  • 以下 CREATE DATABASE 语句创建数据库及其所有对象四天前的状态的克隆,跳过任何数据保留期少于四天的任何表:

    CREATE DATABASE restored_db CLONE my_db
      AT(TIMESTAMP => DATEADD(days, -4, current_timestamp)::timestamp_tz)
      IGNORE TABLES WITH INSUFFICIENT DATA RETENTION;
    
    Copy

删除和恢复对象

弃用对象

删除表、架构或数据库时,不会立即替换或从系统中移除。相反,会在对象的数据保留期内保留,在此期间可以恢复对象。一旦删除的对象被移动到 故障安全,您将无法恢复它们。

若要删除表、架构或数据库,请使用以下命令:

备注

删除对象后,创建同名对象不会恢复该对象。相反,会创建对象的新版本。原来的、被删除的版本仍然可用,可以恢复。

恢复删除的对象会将对象恢复到原位(即不会创建新对象)。

列出删除的对象

可以使用指定了 HISTORY 关键字的以下命令列出删除的表、架构和数据库:

例如:

SHOW TABLES HISTORY LIKE 'load%' IN mytestdb.myschema;

SHOW SCHEMAS HISTORY IN mytestdb;

SHOW DATABASES HISTORY;
Copy

输出包括所有被删除的对象和一个附加 DROPPED_ON 列,该列显示删除对象的日期和时间。如果一个对象被多次删除,则该对象的每个版本都将作为单独的行包含在输出中。

备注

经过对象的保留期并清除对象后,它将不再显示在 SHOW <object_type> HISTORY 输出中。

恢复对象

可以使用以下命令恢复尚未从系统中清除的已删除对象(即对象显示在 SHOW <object_type> HISTORY 输出中):

调用 UNDROP 会将对象恢复到发出 DROP 命令之前的最新状态。

例如:

UNDROP TABLE mytable;

UNDROP SCHEMA myschema;

UNDROP DATABASE mydatabase;
Copy

备注

如果已存在同名对象,UNDROP 将失败。您必须重命名现有对象,这样您就可以恢复该对象的先前版本。

访问控制要求和名称解析

与删除对象类似,用户必须具有对象的 OWNERSHIP 权限才能恢复该对象。此外,用户必须对删除的对象将恢复到的数据库或架构的对象类型具有 CREATE 权限。

仅当前架构或当前数据库支持恢复表和架构,即使指定了完全限定的对象名称也是如此。

示例:多次删除和恢复表

在以下示例中,mytestdb.public 架构包含两个表:loaddata1proddata1loaddata1 表将被删除并重新创建两次,从而创建该表的三个版本:

  • 当前版本

  • 第二个(即最新的)删除版本

  • 第一个删除版本

然后,该示例演示了如何恢复表的两个删除版本:

  1. 首先,将具有相同名称的当前表重命名为 loaddata3。这样就可以根据时间戳恢复已删除表的最新版本。

  2. 然后,将恢复表的最新删除版本。

  3. 恢复的表将重命名为 loaddata2,以便能够恢复删除的表的第一个版本。

  4. 最后,将恢复删除的表的第一个版本。

SHOW TABLES HISTORY;

+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
| created_on                      | name      | database_name | schema_name | kind  | comment | cluster_by | rows | bytes | owner  | retention_time | dropped_on                      |
|---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------|
| Tue, 17 Mar 2016 17:41:55 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 48   | 16248 | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:51:30 -0700 | PRODDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 12   | 4096  | PUBLIC | 1              | [NULL]                          |
+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+

DROP TABLE loaddata1;

SHOW TABLES HISTORY;

+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
| created_on                      | name      | database_name | schema_name | kind  | comment | cluster_by | rows | bytes | owner  | retention_time | dropped_on                      |
|---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------|
| Tue, 17 Mar 2016 17:51:30 -0700 | PRODDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 12   | 4096  | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:41:55 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 48   | 16248 | PUBLIC | 1              | Fri, 13 May 2016 19:04:46 -0700 |
+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+

CREATE TABLE loaddata1 (c1 number);
INSERT INTO loaddata1 VALUES (1111), (2222), (3333), (4444);

DROP TABLE loaddata1;

CREATE TABLE loaddata1 (c1 varchar);

SHOW TABLES HISTORY;

+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
| created_on                      | name      | database_name | schema_name | kind  | comment | cluster_by | rows | bytes | owner  | retention_time | dropped_on                      |
|---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------|
| Fri, 13 May 2016 19:06:01 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 0    | 0     | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:51:30 -0700 | PRODDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 12   | 4096  | PUBLIC | 1              | [NULL]                          |
| Fri, 13 May 2016 19:05:32 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 4    | 4096  | PUBLIC | 1              | Fri, 13 May 2016 19:05:51 -0700 |
| Tue, 17 Mar 2016 17:41:55 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 48   | 16248 | PUBLIC | 1              | Fri, 13 May 2016 19:04:46 -0700 |
+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+

ALTER TABLE loaddata1 RENAME TO loaddata3;

UNDROP TABLE loaddata1;

SHOW TABLES HISTORY;

+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
| created_on                      | name      | database_name | schema_name | kind  | comment | cluster_by | rows | bytes | owner  | retention_time | dropped_on                      |
|---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------|
| Fri, 13 May 2016 19:05:32 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 4    | 4096  | PUBLIC | 1              | [NULL]                          |
| Fri, 13 May 2016 19:06:01 -0700 | LOADDATA3 | MYTESTDB      | PUBLIC      | TABLE |         |            | 0    | 0     | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:51:30 -0700 | PRODDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 12   | 4096  | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:41:55 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 48   | 16248 | PUBLIC | 1              | Fri, 13 May 2016 19:04:46 -0700 |
+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+

ALTER TABLE loaddata1 RENAME TO loaddata2;

UNDROP TABLE loaddata1;

+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
| created_on                      | name      | database_name | schema_name | kind  | comment | cluster_by | rows | bytes | owner  | retention_time | dropped_on                      |
|---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------|
| Tue, 17 Mar 2016 17:41:55 -0700 | LOADDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 48   | 16248 | PUBLIC | 1              | [NULL]                          |
| Fri, 13 May 2016 19:05:32 -0700 | LOADDATA2 | MYTESTDB      | PUBLIC      | TABLE |         |            | 4    | 4096  | PUBLIC | 1              | [NULL]                          |
| Fri, 13 May 2016 19:06:01 -0700 | LOADDATA3 | MYTESTDB      | PUBLIC      | TABLE |         |            | 0    | 0     | PUBLIC | 1              | [NULL]                          |
| Tue, 17 Mar 2016 17:51:30 -0700 | PRODDATA1 | MYTESTDB      | PUBLIC      | TABLE |         |            | 12   | 4096  | PUBLIC | 1              | [NULL]                          |
+---------------------------------+-----------+---------------+-------------+-------+---------+------------+------+-------+--------+----------------+---------------------------------+
Copy
语言: 中文