푸시다운 최적화 및 데이터 가시성

Snowflake는 푸시다운 최적화를 통해 행을 필터링하여 더 빠르고 효율적으로 쿼리를 처리할 수 있도록 지원합니다. 하지만 필터를 재정렬할 수 있는 방식으로 인해 푸시다운으로 표시하고 싶지 않은 데이터가 노출될 수 있습니다.

이 항목에서는 푸시다운과 푸시다운이 어떻게 중요한 데이터를 노출하는지에 대해 설명합니다. 중요한 데이터가 표시되지 않도록, Secure UDF와 저장 프로시저로 민감한 정보 보호하기 의 설명과 같이 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

대부분의 사용자는 테이블에 직접 액세스할 수 없습니다. 대신에 사용자에게 다음 두 역할 중 하나가 할당됩니다.

  • 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가 이 쿼리를 처리하는 데 사용할 수 있는 최소한 두 가지의 방법이 있습니다.

  • 방법 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 이 되므로 이 문은 0으로 나누기 오류를 일으킵니다. physical_health_view 권한이 있는 사용자는 정신 건강 문제가 있는 사람에 대한 행을 보지 못하지만, 정신 건강 범주에 속한 사람이 한 명 이상 존재한다고 추론할 수 있습니다.

이 기법이 항상 기본 세부 정보를 노출시키는 것으로 귀결되는 것은 아닙니다. 뷰(또는 UDFs)를 작성하는 방법뿐 아니라 쿼리 플래너가 선택하는 사항에 크게 의존합니다. 그러나 이 예에서는 사용자가 직접 볼 수 없는 행에 대한 정보를 추론할 수 있음을 보여줍니다.