4
votes

réduire la quantité de données analysées par Athena lors de l'utilisation des fonctions d'agrégation

La requête ci-dessous analyse 100 Mo de données.

select * from table where column1 = 'val' and partition_id in (select max(partition_id) from table);

Cependant, la requête ci-dessous analyse 15 Go de données (il y a plus de 90 partitions)

select * from table where column1 = 'val' and partition_id = '20190309';


3 commentaires

Athena est basée sur Presto. Dans Presto, cela sera amélioré avec le filtrage dynamique ( github.com/prestosql/presto/issues/52 ). Bien que ce cas de requêtes bénéficierait davantage d'un chemin d'exécution différent, où vous exécutez une partie de la requête et re-planifiez le reste ( github.com/prestosql/presto/issues/684 ).


Merci @ PiotrFindeisen, suggérez-vous que la dernière partition doit être récupérée en premier et transmise à la deuxième requête en tant que valeur?


Actuellement, oui, je pense que oui. Vous pouvez commenter sous ce problème pour décrire plus en détail votre cas d'utilisation.


3 Réponses :


8
votes

Il y a deux problèmes ici. L'efficacité de la sous-requête scalaire ci-dessus select max (partition_id) from table , et celle signalée par @PiotrFindeisen autour du filtrage dynamique.

Le premier problème est que les requêtes sur les clés de partition d'un Les tables Hive sont beaucoup plus complexes qu'elles n'y paraissent. La plupart des gens penseraient que si vous voulez la valeur maximale d'une clé de partition, vous pouvez simplement exécuter une requête sur les clés de partition, mais cela ne fonctionne pas car Hive permet aux partitions d'être vides (et il autorise également les fichiers non vides qui ne contiennent aucune ligne). Plus précisément, la sous-requête scalaire ci-dessus select max (partition_id) from table nécessite Trino (anciennement PrestoSQL) pour trouver la partition max contenant au moins une ligne. La solution idéale serait d'avoir des statistiques parfaites dans Hive, mais à part cela, le moteur aurait besoin d'avoir une logique personnalisée pour hive qui ouvre les fichiers des partitions jusqu'à ce qu'il en trouve une non vide.

Si vous êtes êtes sûr que votre entrepôt ne contient pas de partitions vides (ou si vous êtes d'accord avec les implications de cela), vous pouvez remplacer la sous-requête scalaire par une sur la table $ partitions cachée "

select * 
from table 
where column1 = 'val' and 
    partition_id = (select max(partition_id) from "table$partitions");

Le deuxième problème est celui signalé par @PiotrFindeisen, et il a à voir avec la façon dont les requêtes sont planifiées et exécutées. La plupart des gens regarderaient la requête ci-dessus, voir que le moteur devrait de toute évidence, déterminez la valeur de select max (partition_id) de "table $ partitions" pendant la planification, insérez-la dans le plan, puis continuez avec l'optimisation. Malheureusement, c'est une décision assez complexe à prendre de manière générique , donc le moteur modélise simplement cela comme une jointure de diffusion, où une partie de l'exécution calcule cette valeur, et diffuse la valeur au reste des travailleurs. Le problème est que le reste de l'exécution n'a aucun moyen d'ajouter ces nouvelles informations dans le traitement existant, il analyse donc simplement toutes les données, puis filtre les valeurs que vous essayez d'ignorer. Un projet est en cours pour ajouter ce filtrage dynamique , mais ce n'est pas encore terminé.

Cela signifie que le mieux que vous puissiez faire aujourd'hui est d'exécuter deux requêtes distinctes: une pour obtenir le max partition_id et une seconde avec la valeur en ligne.

BTW, la table cachée "$ partitions" a été ajoutée dans Presto 0.199 , et nous avons corrigé quelques bugs mineurs dans 0.201 . Je ne sais pas sur quelle version Athena est basée, mais je pense qu'elle est assez obsolète (la version actuelle au moment où j'écris cette réponse est 309 .


4 commentaires

Merci @Dain Sundstrom. Je vais essayer ça. La table contiendra toujours 1 ou plusieurs partitions dans mon cas.


Bien que ce soit une excellente réponse expliquant les détails et pourquoi ce n'est pas aussi facile que cela puisse paraître à première vue, la suggestion d'utiliser … $ partitions ne fonctionne pas dans Athena car elle est basée sur Presto 0.172.


J'ai pu trouver une solution en utilisant information_schema .__ internal_partitions__ basée sur cette réponse pour répondre le premier problème que vous avez mentionné. Vraiment dommage qu'Athena / Presto n'ait toujours pas de solution pour le deuxième problème :(


Après un peu plus de piratage, j'ai également pu trouver une atténuation partielle pour le deuxième problème qui limite au moins la quantité de données analysées (publiée comme réponse ci-dessous). Cela fonctionne assez bien pour mon cas d'utilisation spécifique, bien que cela puisse ne pas fonctionner pour tous les cas d'utilisation et je ne suis pas sûr à 100% de toutes les représailles liées à l'utilisation de cette table information_schema .__ internal_partitions_ .



2
votes

MODIFIER : Presto a supprimé la table __internal_partitions__ dans leur 0.193 release donc je vous suggère de ne pas utiliser la solution définie dans la section Requêtes d'agrégation lente pour les clés de partition ci-dessous dans tous les systèmes de production, car Athena met à jour de manière« transparente »les versions de presto. J'ai fini par utiliser la requête naïve SELECT max (partition_date) ... , mais aussi en utilisant la même astuce d'analyse décrite dans la section Manque de filtrage dynamique . C'est environ 3 fois plus lent que d'utiliser la table __internal_partitions__ , mais au moins ça ne cassera pas quand Athena décidera de mettre à jour sa version précédente.

----- Original Post -----

J'ai donc trouvé un moyen assez piraté d'accomplir cela pour les partitions basées sur la date sur de grands ensembles de données lorsque vous avez seulement besoin de regarder en arrière sur quelques partitions de données pour une correspondance sur le max, cependant, veuillez noter que je ne suis pas sûr à 100% de la fragilité de l'utilisation de la table information_schema .__ internal_partitions__ .

Comme @Dain l'a noté ci-dessus, il y en a vraiment deux problèmes. Le premier étant la lenteur d'une agrégation de la requête max (partition_date), et le second étant le manque de support de Presto pour le filtrage dynamique.

Requêtes d'agrégation lentes pour les clés de partition

À résoudre le premier problème, j'utilise la table information_schema .__ internal_partitions__ qui me permet d'obtenir des agrégations rapides sur les partitions d'une table sans scanner les données à l'intérieur des fichiers. (Notez que partition_value , partition_key et partition_number dans les requêtes ci-dessous sont tous des noms de colonne de la table __internal_partitions__ et non liée aux colonnes de votre table)

Si vous n'avez qu'une seule clé de partition pour votre table, vous pouvez faire quelque chose comme:

SELECT * FROM "DATABASE_NAME"."TABLE_NAME"
WHERE partition_date >= cast(date '2019-06-25' - interval '3' day as varchar) -- Will only scan partitions from 3 days before '2019-06-25'
AND partition_date = (
  -- Insert the partition aggregation query from above here
)

Mais si vous avoir plusieurs clés de partition, vous aurez besoin de quelque chose de plus comme ceci:

SELECT max(partition_date) as latest_partition_date from (
  SELECT max(case when partition_key = 'partition_date' then partition_value end) as partition_date, max(case when partition_key = 'another_partition_key' then partition_value end) as another_partition_key
  FROM information_schema.__internal_partitions__
  WHERE table_schema = 'DATABASE_NAME' AND table_name = 'TABLE_NAME'
  GROUP BY partition_number
)
WHERE
  -- ... Filter down by values for e.g. another_partition_key
)

Ces requêtes devraient s'exécuter assez rapidement (les miennes s'exécutent en 1 à 2 secondes environ) sans parcourir le réel données dans les fichiers, mais encore une fois, je ne sais pas s'il y a des pièges à utiliser cette approche.

Manque de filtrage dynamique

Je suis en mesure d'atténuer les pires effets du deuxième problème pour mon cas d'utilisation spécifique car je m'attends à ce qu'il y ait toujours une partition dans un laps de temps limité à partir de la date actuelle (par exemple, je peux garantir que tout problème de production de données ou de chargement de partition sera résolu dans les 3 jours ). Il s'avère qu'Athena effectue un pré-traitement lors de l'utilisation des fonctions datetime de presto a>, donc cela n'a pas les mêmes types de problèmes avec le filtrage dynamique que l'utilisation d'une sous-requête.

Vous pouvez donc modifier votre requête pour limiter jusqu'où elle va chercher le maximum réel en utilisant le datetime fonctionne de sorte que la quantité de données analysées soit limitée.

SELECT max(partition_value) FROM information_schema.__internal_partitions__
WHERE table_schema = 'DATABASE_NAME' AND table_name = 'TABLE_NAME'


1 commentaires

Trino dispose désormais d'un filtrage dynamique btw.



2
votes

Je ne sais pas si c'est toujours pertinent, mais je viens de découvrir:

Au lieu de:

select a.* from table a 
inner join (select max(partition_id) max_id from table) b on a.partition_id=b.max_id
where column1 = 'val';

Utilisation:

select * from table where column1 = 'val' and partition_id in (select max(partition_id) from table);

Je pense que cela a quelque chose à voir avec l'optimisation des jointures pour utiliser des partitions.


1 commentaires

Merci, je vais essayer ça !!