Optimisation du pushdown et visibilité des données

Grâce à l’optimisation pushdown, Snowflake contribue à rendre le traitement des requêtes plus rapide et plus efficace en filtrant les lignes. Toutefois, en raison de la manière dont les filtres peuvent être réorganisés, le pushdown peut exposer des données que vous ne souhaitez peut-être pas rendre visibles.

Cette rubrique décrit le pushdown et comment il peut exposer des données sensibles. Pour éviter que des données sensibles ne deviennent visibles, vous pouvez sécuriser une UDF comme décrit dans Protection des informations sensibles avec les UDFs et les procédures stockées sécurisées.

Dans ce chapitre :

Qu’est-ce que le pushdown ?

La pushdown améliore les performances en filtrant les lignes inutiles le plus tôt possible pendant le traitement de la requête. Le pushdown peut également réduire la consommation de mémoire. Toutefois, le pushdown peut permettre d’exposer indirectement des données confidentielles.

Prenons la requête suivante :

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

Une approche pour traiter la requête est la suivante :

  1. Lire toutes les lignes de la table dans la mémoire (c’est-à-dire exécuter la clause FROM).

  2. Analysez les lignes en mémoire, en filtrant toutes les lignes qui ne correspondent pas à New York (c’est-à-dire exécutez la clause WHERE).

  3. Sélectionnez col1 dans les lignes encore en mémoire (c’est-à-dire exécutez la liste SELECT).

Vous pouvez considérer cette démarche comme une stratégie de « chargement d’abord, filtrage plus tard », qui est simple, mais inefficace.

Il est généralement plus efficace d’appliquer le filtre le plus tôt possible. Le filtrage précoce s’appelle « intégrer le filtre le plus étroitement possible au plan de requête », ou plus simplement « pushdown ».

Dans l’exemple de requête ci-dessus, il serait plus efficace d’indiquer au code de balayage de table de ne pas charger les enregistrements qui ne correspondent pas à la clause WHERE. Cela ne fait pas gagner du temps de filtrage (l’emplacement de chaque ligne doit encore être lu une fois), mais cela peut économiser beaucoup de mémoire et réduire le temps de traitement ultérieur car il y a moins de lignes à traiter.

Dans certains cas, vous pouvez traiter les données encore plus efficacement. Par exemple, supposons que les données soient partitionnées par état (c’est-à-dire que toutes les données pour New York se trouvent dans une micro-partition, toutes les données pour la Floride dans une autre micro-partition, et ainsi de suite). Dans ce scénario :

  • Snowflake n’a pas besoin de stocker toutes les lignes en mémoire.

  • Snowflake n’a pas besoin de lire toutes les lignes.

Nous définissons vaguement ceci comme une autre forme de « pushdown ».

Le principe de « pushdown les filtres » s’applique à un large éventail de requêtes. Bien souvent, le filtre le plus sélectif (qui filtre le plus de données) est intégré le plus étroitement possible (exécuté au plus tôt) pour réduire le travail que la requête restante doit effectuer.

Le pushdown peut être combiné à d’autres techniques, telles que le clustering (tri/ordre des données), pour réduire la quantité de données non pertinentes qui doivent être lues, chargées et traitées.

Exemple d’exposition indirecte aux données via le pushdown

L’exemple suivant montre une façon dont le pushdown peut indirectement entraîner l’exposition des détails sous-jacents d’une requête. Cet exemple se concentre sur les vues, mais les mêmes principes s’appliquent aux UDFs.

Supposons qu’il existe une table qui stocke des informations sur les patients :

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');

Il existe deux vues : l’une présente des informations sur la santé mentale et l’autre, des informations sur la santé physique :

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

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

La plupart des utilisateurs n’ont pas d’accès direct à la table. Au lieu de cela, les utilisateurs se voient assigner l’un des deux rôles suivants :

  • MentalHealth, qui a les privilèges pour lire à partir de mental_health_view, ou

  • PhysicalHealth, qui a des privilèges pour lire à partir de physical_health_view.

Supposons maintenant qu’un médecin qui n’a des privilèges que sur les données de santé physique veuille savoir s’il existe actuellement des patients en santé mentale dans la table. Le médecin peut construire une requête similaire à la suivante :

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

Cette requête est équivalente à la suivante :

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

Snowflake peut utiliser (au moins) deux méthodes pour traiter cette requête.

  • Méthode 1 :

    1. Lisez toutes les lignes de la table des patients.

    2. Appliquez le filtre de sécurité de la vue (c’est-à-dire filtrez les lignes pour lesquelles la catégorie n’est pas PhysicalHealth).

    3. Appliquez la clause WHERE dans la requête (c’est-à-dire le filtre basé sur WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1).

  • La méthode 2 modifie l’ordre des filtres, de sorte que la requête s’exécute comme suit :

    1. Lisez toutes les lignes de la table des patients.

    2. Appliquez la clause WHERE dans la requête (c’est-à-dire le filtre basé sur WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1).

    3. Appliquez le filtre de sécurité de la vue (c’est-à-dire filtrez les lignes pour lesquelles la catégorie n’est pas PhysicalHealth).

Logiquement, ces deux séquences semblent équivalentes ; elles renvoient le même ensemble de lignes. Cependant, selon le degré de sélectivité de ces deux filtres, un ordre de traitement pourrait être plus rapide, et le planificateur de requêtes de Snowflake pourrait choisir le plan qui s’exécute plus rapidement.

Supposons que l’optimiseur choisisse le second plan, dans lequel la clause WHERE 1/IFF(category = 'MentalHealth', 0, 1) = 1 est exécutée avant le filtre de sécurité. Si la table des patients contient des lignes dans lesquelles category = 'MentalHealth', alors la fonction IFF renvoie 0 pour cette ligne, et la clause devient effectivement WHERE 1/0 = 1, donc l’instruction provoque une erreur de division par zéro. L’utilisateur avec des privilèges physical_health_view ne voit pas de lignes pour les personnes ayant des problèmes de santé mentale, mais peut comprendre qu’au moins une personne dans la catégorie santé mentale existe.

Notez que cette technique n’aboutit pas toujours à exposer les détails sous-jacents ; elle repose fortement sur les choix que fait le planificateur de requêtes, ainsi que sur la façon dont les vues (ou UDFs) sont écrites. Mais cet exemple montre qu’un utilisateur peut comprendre des informations sur des lignes qu’il ne peut pas voir directement.

Revenir au début