下推优化和数据可见性

通过下推优化,Snowflake 通过筛选行来帮助更快、更高效地处理查询。但是,由于筛选器重新排序的方式,下推可能会公开您可能不希望公开的数据。

本主题介绍下推及其如何公开敏感数据。要防止公开敏感数据,可以按照:doc:`/developer-guide/secure-udf-procedure`中所述来保护 UDF。

本主题内容:

什么是下推?

下推通过在查询处理过程中尽早筛选出不需要的行来提高性能。下推还可以减少内存消耗。但是,下推可能会允许间接公开机密数据。

请考虑以下查询:

SELECT col1
  FROM tab1
  WHERE location = 'New York';
Copy

处理查询的一种方法是:

  1. 将表中的所有行读入内存(即执行 FROM 子句)。

  2. 扫描内存中的行,筛选掉与 New York 不匹配的任何行(即执行 WHERE 子句)。

  3. 从仍留在内存中的行中选择 ``col1``(即执行 SELECT 列表)。

可以将其视为“先加载,后筛选”的策略,这种方法很简单,但效率低下。

尽早筛选通常更有效。早期筛选称为“将筛选器向下推入查询计划更深处”,或简称为“下推”。

在上面的查询示例中,告诉表扫描代码不要加载与 WHERE 子句不匹配的记录会更有效。这不会节省筛选时间(每行的位置仍必须读取一次),但可以节省大量内存并减少后续处理时间,因为要处理的行更少。

在某些情况下,可以更高效地处理数据。例如,假设数据按州分区(即纽约州的所有数据都在一个微分区中,佛罗里达州的所有数据都在另一个微分区中,依此类推)。在这种情况下:

  • Snowflake 不需要将所有行都存储在内存中。

  • Snowflake 不需要读取所有行。

我们粗略地将其定义为另一种形式的“下推”。

“下推筛选器”的原则适用于各种查询。通常,最具选择性的筛选器(筛选出最多的数据)被推送到最深处(最早执行),以减少剩余查询必须执行的工作。

下推可以与其他技术结合使用,例如群集(对数据进行排序),以减少需要读取、加载和处理的不相关数据量。

通过下推间接公开数据的示例

以下示例演示了下推可能间接导致公开有关查询的基础详细信息的一种方式。此示例重点介绍视图,但相同的原则也适用于 UDFs。

假设有一个存储患者信息的表:

CREATE TABLE patients
  (patient_ID INTEGER,
   category VARCHAR,      -- 'PhysicalHealth' or 'MentalHealth'
   diagnosis VARCHAR
   );

INSERT INTO patients (patient_ID, category, diagnosis) VALUES
  (1, 'MentalHealth', 'paranoia'),
  (2, 'PhysicalHealth', 'lung cancer');
Copy

有两种视图,一种显示心理健康信息,另一种显示身体健康信息:

CREATE VIEW mental_health_view AS
  SELECT * FROM patients WHERE category = 'MentalHealth';

CREATE VIEW physical_health_view AS
  SELECT * FROM patients WHERE category = 'PhysicalHealth';
Copy

大多数用户无法直接访问该表。相反,系统会为用户分配以下两个角色之一:

  • MentalHealth,具有从 mental_health_view 中读取的权限,或者

  • PhysicalHealth,具有从 physical_health_view 中读取的权限。

现在假设一位仅对身体健康数据有权限的医生想知道表中当前是否有任何心理健康患者。医生可以构造类似于以下内容的查询:

SELECT * FROM physical_health_view
  WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1;
Copy

此查询等效于:

SELECT * FROM patients
  WHERE
    category = 'PhysicalHealth' AND
    1/IFF(category = 'MentalHealth', 0, 1) = 1;
Copy

Snowflake 可以使用(至少)两种方法来处理此查询。

  • 方法 1:

    1. 读取患者表中的所有行。

    2. 应用视图的安全筛选器(即筛选掉类别不是 PhysicalHealth 的行)。

    3. 在查询中应用 WHERE 子句(即基于 WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 的筛选器)。

  • 方法 2 更改筛选器的顺序,以便查询按如下方式执行:

    1. 读取患者表中的所有行。

    2. 在查询中应用 WHERE 子句(即基于 WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 的筛选器)。

    3. 应用视图的安全筛选器(即筛选掉类别不是 PhysicalHealth 的行)。

从逻辑上讲,这两个序列似乎是等效的;它们返回相同的行集。但是,根据这两个筛选器的选择性,一个处理顺序可能更快,并且 Snowflake 的查询计划器可能会选择执行速度更快的计划。

假设优化器选择了第二个计划,其中 WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 子句在安全筛选器之前执行。如果患者表中有任何 category = 'MentalHealth' 的行,那么 IFF 函数将为该行返回 0,并且该子句实际上变为 WHERE 1/0 = 1,因此该语句会导致被零除的错误。拥有 physical_health_view 权限的用户看不到任何有心理健康问题的人的行,但可以推断出至少有一个人属于心理健康类别。

请注意,这种技术并不总是会公开底层细节;它在很大程度上依赖于查询规划器所做的选择,以及视图(或 UDFs)的编写方式。但此示例显示,用户可以推断出有关用户无法直接查看的行的信息。

语言: 中文