Otimização de pushdown e visibilidade de dados

Através da otimização pushdown, o Snowflake ajuda a tornar o processamento de consultas mais rápido e eficiente por filtragem de linhas. No entanto, devido à forma como os filtros podem ser reordenados, o pushdown pode expor dados que talvez você não queira que fiquem visíveis.

Este tópico descreve o pushdown e como ele pode expor dados sensíveis. Para evitar que os dados sensíveis fiquem visíveis, você pode tornar um UDF seguro, como descrito em Proteção de informações sensíveis com UDFs e procedimentos armazenados seguros.

Neste tópico:

O que é pushdown?

O pushdown melhora o desempenho filtrando linhas desnecessárias o quanto antes durante o processamento da consulta. O pushdown também pode reduzir o consumo de memória. Entretanto, o pushdown pode permitir que dados confidenciais sejam expostos indiretamente.

Considere a seguinte consulta:

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

Uma abordagem do processamento de consulta é:

  1. Ler todas as linhas da tabela na memória (ou seja, executar a cláusula FROM).

  2. Varrer as linhas na memória, filtrando quaisquer linhas que não correspondam a New York (isto é, executar a cláusula WHERE).

  3. Selecionar col1 das linhas que ainda estão na memória (isto é, executar a lista SELECT).

Você pode pensar nisso como uma estratégia de “carregar primeiro, filtrar depois”, que é simples, mas ineficiente.

Geralmente é mais eficiente filtrar o mais cedo possível. A filtragem antecipada é chamada de “empurrar o filtro para baixo para se aprofundar no plano de consulta”, ou simplesmente “pushdown” (empurrar para baixo).

Na consulta de exemplo acima, seria mais eficiente dizer ao código de varredura de tabela para não carregar registros que não correspondem à cláusula WHERE. Isso não economiza tempo de filtragem (a localização de cada linha ainda deve ser lida uma vez), mas pode economizar consideravelmente a memória e reduzir o tempo de processamento subsequente porque há menos linhas para processar.

Em alguns casos, você pode processar os dados com ainda mais eficiência. Por exemplo, suponha que os dados estejam divididos por estado (ou seja, todos os dados para Nova York estão em uma micropartição, todos os dados para a Flórida estão em outra micropartição e assim por diante). Neste cenário:

  • O Snowflake não precisa armazenar todas as linhas na memória.

  • O Snowflake não precisa ler todas as linhas.

Definimos isso vagamente como outra forma de “pushdown”.

O princípio de “empurrar os filtros para baixo” aplica-se a uma ampla gama de consultas. Muitas vezes, o filtro que é o mais seletivo (filtra a maioria dos dados) é empurrado mais para baixo (executado mais cedo) para reduzir o trabalho que a consulta restante deve fazer.

O pushdown pode ser combinado com outras técnicas, como o clustering (classificação/ordenação dos dados), para reduzir a quantidade de dados irrelevantes que precisam ser lidos, carregados e processados.

Exemplo de exposição indireta de dados usando o pushdown

O exemplo a seguir mostra uma maneira que o pushdown poderia resultar indiretamente na exposição de detalhes subjacentes de uma consulta. Este exemplo se concentra nas exibições, mas os mesmos princípios se aplicam a UDFs.

Suponha que haja uma tabela que armazene informações sobre pacientes:

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

Há duas exibições, uma mostra informações sobre a saúde mental e outra sobre a saúde física:

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

A maioria dos usuários não tem acesso direto à tabela. Em vez disso, os usuários são atribuídos a uma de duas funções:

  • MentalHealth, que tem privilégios para ler a partir de mental_health_view, ou

  • PhysicalHealth, que tem privilégios para ler a partir de physical_health_view.

Agora suponha que um médico com privilégios apenas para acessar dados de saúde física queira saber se há atualmente algum paciente com saúde mental na tabela. O médico pode construir uma consulta semelhante à seguinte:

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

Esta consulta é equivalente a:

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

Há (pelo menos) dois métodos que o Snowflake pode usar para processar esta consulta.

  • Método 1:

    1. Ler todas as linhas da tabela de pacientes.

    2. Aplicar o filtro de segurança da exibição (ou seja, filtrar as linhas para as quais a categoria não é PhysicalHealth).

    3. Aplicar a cláusula WHERE na consulta (ou seja, filtro baseado em WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1).

  • O método 2 muda a ordem dos filtros para que a consulta seja executada da seguinte forma:

    1. Ler todas as linhas da tabela de pacientes.

    2. Aplicar a cláusula WHERE na consulta (ou seja, filtro baseado em WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1).

    3. Aplicar o filtro de segurança da exibição (ou seja, filtrar as linhas para as quais a categoria não é PhysicalHealth).

Logicamente, essas duas sequências parecem equivalentes; elas retornam o mesmo conjunto de linhas. Entretanto, dependendo da seletividade desses dois filtros, uma ordem de processamento pode ser mais rápida, e o planejador de consultas do Snowflake pode escolher o plano que executa mais rapidamente.

Suponha que o otimizador escolha o segundo plano, no qual a cláusula WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 é executada antes do filtro de segurança. Se a tabela de pacientes tem alguma linha na qual category = 'MentalHealth', então a função IFF retorna 0 para aquela linha, e a cláusula efetivamente se torna WHERE 1/0 = 1, então a instrução causa um erro de divisão por zero. O usuário com privilégios physical_health_view não vê uma linha para pessoas com problemas de saúde mental, mas pode deduzir que existe pelo menos uma pessoa na categoria de saúde mental.

Note que esta técnica nem sempre resulta na exposição dos detalhes subjacentes; ela depende muito das escolhas que o planejador da consulta faz e de como as exibições (ou UDFs) são escritas. Mas este exemplo mostra que um usuário pode deduzir informações sobre linhas que o usuário não pode visualizar diretamente.