访问历史记录¶
本主题介绍了 Snowflake 中用户访问历史记录的概念。
概述¶
Snowflake 中的访问历史记录是指用户查询读取数据的时间,以及 SQL 语句执行从源数据对象到目标数据对象的数据写入操作(如 INSERT、UPDATE、DELETE 以及 COPY 命令变体)的时间。通过查询 ACCOUNT_USAGE 和 ORGANIZATION_USAGE 架构中的 ACCESS_HISTORY 视图,可以找到用户访问历史记录。这些视图中的记录有助于监管合规性审计,并提供有关常用和频繁访问的表和列的见解,因为用户(即查询操作员)、查询、表或视图、列和数据之间存在直接关联。
ACCESS_HISTORY 视图中的每一行均包含对应于每条 SQL 语句的一条记录。该记录包含以下类型的信息:
查询直接和间接访问的 源列,例如查询数据来自的基础表。
用户在查询结果中看到的 投影列,例如 SELECT 语句中指定的列。
用于确定查询结果但未投影的列,例如用于筛选结果的 WHERE 子句中的列。
例如:
C1 和 C2 列是视图直接访问的源列,这些列记录在 ACCESS_HISTORY 视图的
base_objects_accessed列中。C3 列用于筛选视图包含的行,这些行记录在 ACCESS_HISTORY 视图的
base_objects_accessed列中。VC1 和 VC2 列是用户在查询视图时看到的投影列,
SELECT * FROM v1;,这些列记录在 ACCESS_HISTORY 视图的direct_objects_accessed列中。
同样的行为适用于 WHERE 子句中的键列。例如:
创建视图需要两个不同的表:
bt(基表)和jt(联接表)。基表中的 C1、C2 和 C3 列以及联接表中的 C1 列都记录在 ACCESS_HISTORY 视图的
base_objects_accessed列中。VC1、VC2 和 C1 列是用户在查询视图时看到的投影列,
SELECT * FROM join_v;,这些列记录在 ACCESS_HISTORY 视图的direct_objects_accessed列中。
备注
Account Usage QUERY_HISTORY 视图中的记录并非始终会记录在 ACCESS_HISTORY 视图中。SQL 语句的结构决定 Snowflake 是否在 ACCESS_HISTORY 视图中记录一个条目。
有关 ACCESS_HISTORY 视图中 Snowflake 支持的读取和写入操作的详细信息,请参阅视图的 使用说明。
跟踪读取和写入操作¶
ACCOUNT_USAGE 和 ORGANIZATION_USAGE 架构中的 ACCESS_HISTORY 视图包括以下列:
读取操作通过前五列进行跟踪,而最后一列(即 objects_modified 列)指定涉及 Snowflake 列、表和暂存区的数据写入信息。
Snowflake 中的查询及数据库对象的创建方式决定了 Snowflake 在 direct_objects_accessed、base_objects_accessed 和 objects_modified 列中返回的信息。
同样,如果查询引用受行访问策略保护的对象或者受掩码策略保护的列,Snowflake 会在 policies_referenced 列中记录策略信息。
object_modified_by_ddl 列记录对数据库、架构、表、视图和列执行的 DDL 操作。这些操作还包括针对表或视图指定行访问策略、针对列指定掩码策略以及针对对象或列指定标签更新(例如设置标签、更改标签值)的语句。
parent_query_id 和 root_query_id 列记录对应于以下内容的查询 IDs:
对另一个对象执行读取或写入操作的查询。
对调用存储过程的对象执行读取或写入操作的查询,包括嵌套存储过程调用。有关详细信息,请参阅 祖先查询 (本主题内容)。
有关列详细信息,请参阅 ACCESS_HISTORY 视图中的 Columns 部分。
读取¶
为了解读取查询以及 ACCESS_HISTORY 视图如何记录此信息,请考虑以下场景:
一系列对象:
base_table»view_1»view_2»view_3。对于
view_2的读取查询,例如:
在本示例中,Snowflake 返回的是:
direct_objects_accessed列中的view_2,因为查询指定了view_2。base_objects_accessed列中的base_table,因为这是view_2中数据的原始来源。
请注意, view_1 和 view_3 未包含在 direct_objects_accessed 和 base_objects_accessed 列中,因为查询中并未包含这些视图,它们也不是用作 view_2 中数据来源的基本对象。
写入¶
为了解写入操作以及 ACCESS_HISTORY 视图如何记录此信息,请考虑以下场景:
一个数据源:
base_table基于该数据源创建一个表(即 CTAS):
在本示例中,Snowflake 返回的是:
base_objects_accessed和direct_objects_accessed列中的base_table,因为该表是直接访问的,并且是数据的来源。objects_modified列中的table_1,包括在创建表时写入的列。
支持的操作¶
有关 ACCESS_HISTORY 视图支持的读取和写入操作的完整说明,请参阅 ACCESS_HISTORY 视图 中的使用说明部分。
单个请求中的多个语句。¶
Snowflake 支持将多个语句作为单个请求同时执行。在访问历史记录中跟踪请求的方式取决于它是在 Snowsight 中执行,还是以编程方式执行。
使用 Snowsight 执行多个语句时,它一次运行一个查询,并返回上次执行的查询的
query_id。您可以在 ACCESS_HISTORY 视图中查找所有已执行的语句及其返回值。Snowflake Python 连接器或 Snowflake SQL API 之类的功能会将多个 SQL 语句合并到一个请求中,并为所有语句返回一个
query_id。这个数字实际上是所有单个语句的父查询 ID。要返回包含请求的每个语句的query_id,您必须使用parent_query_id查询 ACCESS_HISTORY。例如,如果请求返回query_id = 6789,那么您可以通过执行以下命令返回各个语句的查询 ID:
优势¶
Snowflake 中的访问历史记录提供了与读取和写入操作相关的以下优势:
- 数据发现:
发现未使用的数据,以确定存档还是删除数据。
- 跟踪敏感数据的移动方式:
跟踪数据从外部云存储位置(例如 Amazon S3 桶)到目标 Snowflake 表的移动,反之亦然。
跟踪内部数据从一个 Snowflake 表到其他 Snowflake 表的移动。
跟踪敏感数据的移动后,应用策略(掩码 和 行访问 策略)以保护数据,更新 访问控制设置 以进一步规范对暂存区和表的访问,并设置 标签 以确保可跟踪包含敏感数据的暂存区、表和列,确保满足合规性要求。
- 数据验证:
报告、仪表板和数据可视化产品(如图表和图形)的准确性和完整性得到验证,因为数据可追溯到其原始来源。
数据专员还可在删除或更改给定表或视图之前通知用户。
- 合规性审计:
标识在表或暂存区上执行写入操作的 Snowflake 用户,以及执行写入操作的时间,以满足合规性法规,例如 GDPR (https://gdpr-info.eu/) 和 CCPA (https://oag.ca.gov/privacy/ccpa)。
- 增强整体数据治理:
ACCESS_HISTORY 视图提供了有关访问哪些数据、何时发生数据访问以及所访问的数据如何从数据源对象移动到数据目标对象的更统一的视图。
列沿袭¶
列沿袭(即列的访问历史记录)扩展了 Account Usage ACCESS_HISTORY 视图,以指定在写入操作中数据如何从源列流向目标列。Snowflake 会通过引用源列数据的所有后续表对象(例如 INSERT、MERGE、CTAS)跟踪源列中的数据,前提是沿袭链中的对象未被删除。Snowflake 通过增强 ACCESS_HISTORY 视图中的 objects_modified 列来讲列沿袭置于可访问状态。
列沿袭提供以下优势:
- 保护派生的对象:
数据专员可以轻松地为敏感源列添加 标签,而无需在创建派生对象(例如 CTAS)后执行额外的工作。随后,数据专员可以使用 行访问策略 保护包含敏感列的表,或者使用 掩码策略 或 基于标签的掩码策略 来保护敏感列本身。
- 敏感列的复制频率:
数据隐私官可快速确定包含敏感数据的列的对象数量(例如 1 个表、2 个视图)。通过了解包含敏感数据的列在表对象中出现的次数,数据隐私官即可证明他们满足监管合规标准(例如,满足《欧盟通用数据保护条例》(GDPR) 标准)的情况。
- 根本原因分析:
列沿袭提供了一种追溯数据直至其源的机制,这有助于查明由于数据质量差而导致的故障点,并减少故障排除过程中要分析的列数。
有关列沿袭的其他详细信息,请参阅:
列沿袭 (本主题内容)
掩码和行访问策略引用¶
POLICY_REFERENCED 列指定在表上设置了行访问策略或在列上设置了掩码策略的对象,包括受行访问策略或掩码策略保护的任何中间对象。Snowflake 记录在表或列上强制实施的策略。
考虑以下对象:
t1 » v1 » v2
其中:
t1是基表。v1是基于基表构建的视图。v2是基于v1构建的视图。
如果用户查询 v2,则 policies_referenced 列会记录保护 v2 的行访问策略、保护 v2 中的列的各掩码策略,或同时记录这两种策略(如果适用)。此外,此列还会记录保护 t1 和 v1 的任何掩码或行访问策略。
这些记录有助于数据治理者了解如何访问其受策略保护的对象。
policies_referenced 列还为 ACCESS_HISTORY 视图提供了额外的优势:
识别用户在给定查询中访问的受策略保护的对象。
简化策略的审核流程。
查询 ACCESS_HISTORY 视图后,无需对其他 Account Usage 视图(例如 POLICY_REFERENCES 和 QUERY_HISTORY)进行复杂联接,即可获取有关用户访问的受保护对象和受保护列的信息。
账户级与企业级访问历史记录¶
管理员通过查询账户 ACCOUNT_USAGE 架构中的 ACCESS_HISTORY 视图,来监控账户级访问历史记录。没有与 ACCOUNT_USAGE.ACCESS_HISTORY 视图关联的任何额外成本。
ORGANIZATION_USAGE 架构中的 ACCESS_HISTORY 视图将组织中所有账户的访问历史记录收集到一个视图中,以提供组织级访问历史记录。此 ORGANIZATION_USAGE.ACCESS_HISTORY 视图只能在 组织账户 中找到。
ORGANIZATION_USAGE 架构中的组织级访问历史记录与 ACCOUNT_USAGE 架构中的访问历史记录在以下方面有所不同:
- 其他列:
组织账户中的 ORGANIZATION_USAGE.ACCESS_HISTORY 视图包含其他列,这些列提供与 组织列表 相关的见解。这些列可用于确定使用者的查询访问了哪些附加到组织列表的数据产品,以及这些数据产品是否受到策略(例如掩码策略)的保护。有关更多信息,请参阅 组织列表治理。
- 其他成本:
组织账户中的 ORGANIZATION_USAGE.ACCESS_HISTORY 视图是高级视图,会产生以下成本:
与用于填充 ACCESS_HISTORY 视图的 Serverless Task 关联的计算成本。
与在 ACCESS_HISTORY 视图中存储数据关联的存储成本。
有关这些成本的更多信息,请参阅 与高级视图相关的成本。
受支持对象¶
使用下表来确定当 SQL 语句涉及特定类型的对象时,ACCESS_HISTORY 视图是否包含记录。SQL 语句包括:
数据操作语言 (DML) 语句。例如,用于向表中插入数据的语句。
数据查询语言 (DQL) 语句。例如,使用 SELECT 语句投影数据的语句。
数据定义语言 (DDL) 语句。例如,创建或更改 Snowflake 对象的语句。
对象 |
DML |
DQL |
DDL |
备注 |
|---|---|---|---|---|
DATABASE |
不适用 |
不适用 |
✔ |
|
DYNAMIC TABLE |
部分 |
✔ |
✔ |
仅 ``ALTER DYNAMIC TABLE ... REFRESH``命令支持 DML 功能。 |
EXTERNAL TABLE |
✔ |
✔ |
✔ |
|
AGENT |
不适用 |
不适用 |
✔ |
用于 Cortex 代理对象的 DDL(例如 CREATE AGENT、ALTER AGENT 和 DROP AGENT)。 |
FUNCTION |
不适用 |
✔ |
✔ |
对 DQL 的支持仅限于出现在 SELECT 语句中的函数。 |
ICEBERG TABLE |
部分 |
✔ |
✔ |
对于 Snowflake 管理的 Apache Iceberg™ 表,完全支持(DML、DQL、DDL)仅支持对外部托管的 Apache Iceberg™ 表执行 DQL 和 DDL。 |
LISTING |
不适用 |
不适用 |
✔ |
|
MATERIALIZED VIEW |
不适用 |
✔ |
✔ |
|
MCP SERVER |
不适用 |
不适用 |
✔ |
用于 MCP 服务器的 DDL(例如 CREATE MCP SERVER、ALTER MCP SERVER 和 DROP MCP SERVER)。 |
POLICY |
不适用 |
✔ |
✔ |
对 DDL 的支持体现为:当策略应用于某个对象时,以及通过 SHOW 和 DESCRIBE 命令查询策略元数据时。DQL 支持在查询运行时显示正在强制执行的策略。 |
POSTGRES INSTANCE |
不适用 |
不适用 |
✔ |
用于 Postgres 实例的 DDL(例如 CREATE POSTGRES INSTANCE、ALTER POSTGRES INSTANCE 和 DROP POSTGRES INSTANCE)。 |
PROCEDURE |
不适用 |
✔ |
✔ |
一个过程可以有多个 SQL 语句,每个语句生成单独的记录。 |
ROLE |
不适用 |
不适用 |
✔ |
|
SCHEMA |
不适用 |
不适用 |
✔ |
|
SEQUENCE |
不适用 |
✔ |
不支持 DML 是有意为之。 |
|
SESSION |
不适用 |
不适用 |
✔ |
|
SHARE |
不适用 |
不适用 |
✔ |
|
STAGE |
部分 |
✔ |
对 DML 的支持仅限于使用暂存区作为表的源。对于 DQL,不支持针对暂存区的查询。 |
|
STREAM |
不适用 |
部分 |
✔ |
对 DQL 的支持仅限于将流用作表的数据源。对 DDL 的支持仅限于创建操作。 |
TABLE |
✔ |
✔ |
✔ |
|
TAG |
不适用 |
不适用 |
✔ |
|
VIEW |
不适用 |
✔ |
✔ |
查询 ACCESS_HISTORY 视图¶
以下各部分提供了视图的 ACCESS_HISTORY 查询示例。
请注意,某些查询示例会对 query_start_time 列进行筛选,以提高查询性能。提高性能的另一种选择是在较窄的时间范围内执行查询。
访问历史记录示例¶
读取查询¶
以下小节详细介绍了如何查询 ACCESS_HISTORY 视图,以获取以下用例的读取操作:
获取特定用户的访问历史记录。
根据
object_id(例如表 ID),促进对过去 30 天内敏感数据访问的合规性审计,以回答以下问题:谁访问了数据?
是在何时访问数据的?
访问了哪些列?
返回用户访问历史记录¶
返回用户访问历史记录,按用户和查询开始时间排序,从最近的访问开始。
促进合规性审计¶
以下示例有助于促进合规性审计:
添加
object_id值,以确定在过去 30 天内谁访问过敏感表:使用
32998411400350的object_id值,确定过去 30 天内发生访问的时间:使用
32998411400350的object_id值,确定过去 30 天内访问过哪些列:
写入操作¶
以下小节详细介绍了如何查询 ACCESS_HISTORY 视图,以获取以下用例的写入操作:
将数据从暂存区加载到表中。
将数据从表卸载到暂存区中。
使用 PUT 命令将本地文件上传到暂存区。
使用 GET 命令将数据文件从暂存区检索到本地目录。
跟踪敏感的暂存区数据移动。
将数据从暂存区加载到表¶
将一组值从外部云存储中的数据文件加载到目标表的列中。
direct_objects_accessed 和 base_objects_accessed 列指定已访问外部命名暂存区:
objects_modified 列指定将数据写入表的两列之中:
将数据从表卸载到暂存区¶
将一组值从 Snowflake 表卸载到云存储。
direct_objects_accessed 和 base_objects_accessed 列指定所访问的表列:
objects_modified 列指定要将访问的数据写入到的暂存区:
使用 PUT 命令将本地文件上传到暂存区¶
将数据文件复制到内部(即 Snowflake)暂存区。
direct_objects_accessed 和 base_objects_accessed 列指定所访问文件的本地路径:
objects_modified 列指定要将访问的数据写入的暂存区:
使用 GET 命令将数据文件从暂存区检索到本地目录¶
将数据文件从内部暂存区检索到本地计算机上的目录。
direct_objects_accessed 和 base_objects_accessed 列指定访问的暂存区和本地目录:
objects_modified 列指定将访问的数据写入到的目录:
跟踪敏感的暂存区数据移动¶
跟踪敏感的暂存区数据,了解它按时间顺序执行的一系列查询。
执行以下查询。请注意,其中 5 个语句会访问暂存区数据。因此,在查询 ACCESS_HISTORY 视图以了解暂存区访问权限时,结果集应包含五行。
其中:
T1、T2...``T7`` 指定表的名称。
S1和S2指定暂存区名称。
查询访问历史记录,以确定对 S1 暂存区的访问。
下表显示了
direct_objects_accessed、base_objects_accessed和objects_modified列的数据。
direct_objects_accessed
base_objects_accessed
objects_modified请注意有关查询示例的以下方面:
该查询生成以下与
S1暂存区数据移动相关的结果集:
PATH
TARGET_NAME
TARGET_ID
TARGET_DOMAIN
TARGET_COLUMNS
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1
TEST_DB.TEST_SCHEMA.T1
66564
表
["CONTENT"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.S2
TEST_DB.TEST_SCHEMA.S2
118
暂存区
[]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.T2
TEST_DB.TEST_SCHEMA.T2
66568
表
["NAME","ID"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.T4
TEST_DB.TEST_SCHEMA.T4
66572
表
["ID","NAME"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T3
TEST_DB.TEST_SCHEMA.T3
66570
表
["CUSTOMER_INFO"]
列沿袭¶
下面的示例查询 ACCESS_HISTORY 视图,并使用 FLATTEN 函数展平 objects_modified 列。
作为代表性示例,在 Snowflake 账户中执行以下 SQL 查询以生成下表,其中编号的注释指示了以下内容:
// 1:获取directSources字段和目标列之间的映射。// 2:获取baseSources字段和目标列之间的映射。
返回:
SOURCE_OBJECT_ID
SOURCE_OBJECT_NAME
SOURCE_COLUMN_NAME
SOURCE_COLUMN_TYPE
TARGET_OBJECT_NAME
TARGET_COLUMN_NAME
1
D.S.T0
NAME
BASE
D.S.T1
NAME
2
D.S.V1
NAME
DIRECT
D.S.T1
NAME
跟踪行访问策略引用¶
在表、视图或物化视图上设置行访问策略且无重复时,为每个实例返回一行:
跟踪掩码策略引用¶
在掩码策略保护无重复的列时,为每个实例返回一行。请注意,policies_referenced 列指定的列掩码策略比表上的行访问策略深一级,因此必须执行额外的展平:
跟踪查询中强制执行的策略¶
返回给定时间范围内给定查询的策略更新时间 (POLICY_CHANGED_TIME) 和策略条件 (POLICY_BODY)。
在使用此查询之前,更新 WHERE 子句输入值:
其中:
query_start_time > '2023-07-07'指定开始时间戳。
query_start_time < '2023-07-08'指定结束时间戳。
query_id = '01ad7987-0606-6e2c-0001-dd20f12a9777'指定 Account Usage ACCESS_HISTORY 视图中的查询标识符。
运行查询:
UDFs¶
这些 UDF 示例显示了 Account Usage ACCESS_HISTORY 视图如何记录以下项:
调用 UDF¶
考虑以下 SQL UDF,它计算两个数字的乘积,并假设它存储在名为 mydb.udfs 的架构中:
直接 调用 get_product 会在 direct_objects_accessed 列中记录 UDF 详细信息:
此示例类似于 调用存储过程 (本主题内容)。
UDF 及 INSERT DML¶
考虑以下 INSERT 语句,它用于更新名为 mydb.tables.t1 的表中名为 1 和 2 的列:
ACCESS_HISTORY 视图将 get_product 函数记录在以下位置:
direct_objects_accessed列,因为函数在语句中 SQL 显式指定,以及directSources数组中的objects_modified列,因为函数是插入到列中的值的来源。
同样, t1 表记录在以下列中:
direct_objects_accessed
objects_modified
跟踪由 DDL 操作修改的对象¶
使用 ALLOWED_VALUES 创建标签¶
创建标签:
列值:
备注
如果在创建标签时没有指定允许的值,则 properties 字段为空数组(即 {})。
使用标签和掩码策略创建表¶
创建具有列上的掩码策略、列上的标签和表上的标签的表:
列值:
在标签上设置掩码策略¶
在标签上设置掩码策略(即 基于标签的掩码):
列值:
交换表¶
将名为 t2 的表交换为名为 t3 的表:
请注意视图中的两条不同记录。
记录 1:
记录 2:
删除掩码策略¶
删除掩码策略:
列值:
备注
列值具有代表性,适用于对标签和行访问策略执行的 DROP 操作。
properties字段是一个空数组,在执行 DROP 操作之前不会提供有关策略的任何信息。
跟踪列上的标签引用¶
查询 object_modified_by_ddl 列以监控标签在列上是如何设置的。
以表管理员的身份在列上设置标签、取消设置标签,然后使用不同的字符串值更新标签:
以数据工程师的身份更改标签值:
查询 ACCESS_HISTORY 视图以监视更改:
返回:
调用存储过程¶
考虑以下存储过程,并假定它存储在名为 mydb.procedures 的架构中:
直接 调用 my_procedure 会导致在 direct_objects_accessed 和 base_objects_accessed 列中记录过程详细信息,如下所示:
此示例类似于 调用 UDF (本主题内容)。
使用存储过程的祖先查询¶
您可以使用 parent_query_id 和 root_query_id 列了解存储过程调用之间的关系。
假设您有三个不同的存储过程语句并按以下顺序运行这些语句:
对 ACCESS_HISTORY 视图的查询将按如下方式记录信息:
第一行对应的是调用名为
myproc_parent的第二个存储过程,如direct_objects_accessed列所示。parent_query_id和root_query_id列返回 NULL,因为您直接调用了此存储过程。第二行对应的是调用名为
myproc_child的第一个存储过程的查询,如direct_objects_accessed column所示。parent_query_id和root_query_id列返回相同的查询 ID,因为调用myproc_child的查询是由调用 :code:`myproc_parent`(直接调用)的查询启动的。第三行对应的是访问
myproc_child过程中名为mytable的表的查询,如direct_objects_accessed列中所示。parent_query_id列返回访问mytable的查询的查询 ID,对应于调用myproc_child。该存储过程由调用myproc_parent启的查询动,该查询显示在root_query_id列中。
序列¶
考虑以下创建序列的 SQL 语句:
创建此序列会在访问历史记录中生成以下条目:
联接¶
查询中的联接操作在访问历史记录中会以 joinObject 的形式显示在 :code:direct_accessed_objects 列中。joinObject 不会出现在其他列中,因为访问历史记录只跟踪查询中明确提及的联接。
例如,考虑以下联接表 t1 和表 t2 的查询:
执行此查询会导致 direct_accessed_objects 列中的 t1 对象出现以下内容:
备注
在这个例子中,访问历史记录中不会包含 ``t2``对象的 ``joinObject`` 信息,因为该信息已由表 ``t1`` 的 ``joinObject`` 提供,属于冗余数据。