プッシュダウンの最適化およびデータの可視化

Snowflakeは、プッシュダウンの最適化を介して行をフィルターすることで、クエリ処理をより高速かつ効率的にするのに役立ちます。ただし、フィルターを並べ替える方法が原因で、表示したくないデータがプッシュダウンにより公開される可能性があります。

このトピックでは、プッシュダウンと、機密データが公開される方法について説明します。機密データが表示されないようにするには、 セキュア UDFs とストアドプロシージャの使用による機密情報の保護 で説明されているように UDF を保護します。

このトピックの内容:

プッシュダウンとは何ですか?

クエリ処理中に不要な行をできるだけ早くフィルタリングするプッシュダウンにより、パフォーマンスは向上します。プッシュダウンにより、メモリ消費も削減できます。ただし、プッシュダウンにより、機密データが間接的に公開される可能性があります。

次のクエリを検討してください。

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

次にクエリ処理の例を示します。

  1. テーブルからすべての行をメモリに読み込みます( FROM 句を実行)。

  2. メモリ内の行をスキャンし、 New York に一致しない行を除外します( WHERE 句を実行)。

  3. メモリに残っている行から col1 を選択します( SELECT リストを実行)。

これは「最初にロードし、後でフィルタリングする」戦略と考えることができます。簡単ではありますが、非効率的です。

通常、できるだけ早くフィルタリングする方が効率的です。早期フィルタリングは、「フィルターをクエリプランに深くプッシュする」、または単に「プッシュダウン」と呼ばれます。

上記のクエリ例では、 WHERE 句に一致しない記録をロードしないようにテーブルスキャンコードに指示する方が効率的です。これはフィルタリング時間を節約しません(すべての行の場所を1回読み取る必要あり)が、処理する行が少ないため、かなりのメモリを節約し、後続の処理時間を短縮できます。

場合によっては、データをさらに効率的に処理できます。例えば、データが州ごとに分割されていると仮定します(ニューヨークのすべてのデータが1つのマイクロパーティションにあり、フロリダのすべてのデータが別のマイクロパーティションにあるなど)。このシナリオでは:

  • 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

2つのビューがあり、1つは精神的健康情報を示し、もう1つは身体的健康情報を示します。

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

ほとんどのユーザーはテーブルに直接アクセスできません。代わりに、ユーザーには次の2つのロールのいずれかが割り当てられます。

  • mental_health_view から読み取る権限を持つ MentalHealth

  • physical_health_view から読み取る権限を持つ PhysicalHealth

ここで、身体的健康データのみの権限を持つ医師が、現在精神衛生上の患者がテーブルにいるかどうかを知りたいと仮定します。医師は次のようなクエリを作成できます。

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では、このクエリを処理するために使用できる(少なくとも)2つの方法があります。

  • 方法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 ではない行を除外)。

論理的には、これら2つのシーケンスは同等に見えます。同じ行セットを返します。ただし、これら2つのフィルターの選択の程度によっては、1つの処理順序が速くなる場合があり、Snowflakeのクエリプランナーは、より高速に実行されるプランを選択する場合があります。

オプティマイザーが2番目のプランを選択するとします。このプランでは、句 WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 がセキュリティフィルターの前に実行されます。患者(patients)テーブルに category = 'MentalHealth' の行がある場合、 IFF 関数はその行に対して0を返し、句は事実上 WHERE 1/0 = 1 になるため、ステートメントはゼロ除算エラーを引き起こします。 physical_health_view 権限を持つユーザーには、精神的健康(mental health)に問題のある人の行は表示されませんが、精神的健康カテゴリに少なくとも1人は存在すると推測できます。

この手法は、基礎となる詳細を常に公開するとは限りません。ビュー(または UDFs)の記述方法と同様に、クエリプランナーが行う選択に大きく依存します。しかし、この例は、直接表示できない行に関する情報をユーザーが推測できることを示しています。