web-dev-qa-db-fra.com

Hive - Jointure efficace de deux tables

Je rejoins deux grandes tables dans Hive (l'une compte plus d'un milliard de lignes, l'autre environ 100 millions de lignes), comme suit:

create table joinedTable as select t1.id, ... from t1 join t2 ON (t1.id = t2.id);

J'ai regroupé les deux tables de la même manière, en regroupant par ID dans 100 compartiments pour chacun, mais la requête prend toujours beaucoup de temps. 

Des suggestions sur la façon d'accélérer cela? 

13
maia

Au fur et à mesure que vous regroupez les données à l'aide des clés de jointure, vous pouvez utiliser la jointure de carte de seau. Pour cela, la quantité de compartiments dans une table doit être un multiple de la quantité de compartiments dans l'autre table. Il peut être activé en exécutant set Hive.optimize.bucketmapjoin=true; avant la requête. Si les tables ne remplissent pas les conditions, Hive exécutera simplement la jointure interne normale.

Si les deux tables ont le même nombre de compartiments et que les données sont triées par les clés de compartiment, Hive peut exécuter la jointure Tri-Fusion plus rapide. Pour l'activer, vous devez exécuter les commandes suivantes:

set Hive.input.format=org.Apache.hadoop.Hive.ql.io.BucketizedHiveInputFormat;
set Hive.optimize.bucketmapjoin=true;
set Hive.optimize.bucketmapjoin.sortedmerge=true;

Vous pouvez trouver des visualisations des différentes techniques de jointure sous https://cwiki.Apache.org/confluence/download/attachments/27362054/Hive+Summit+2011-join.pdf .

15
Adrian Lange

À mon avis, la réponse est un peu plus compliquée que celle proposée par @Adrian Lange. 

Vous devez d’abord comprendre une différence très importante entre BucketJoin et Sort-Merge Bucket Join (SMBJ): 

Pour effectuer un bucketjoin _ "la quantité de compartiments dans une table doit être un multiple de la quantité de compartiments dans l'autre table", comme indiqué précédemment et Hive.optimize.bucketmapjoin doit en outre être défini sur true.
En émettant une jointure, Hive le convertira en un bucketjoin si la condition ci-dessus est remplie MAIS faites attention à ce que Hive n'imposera pas le seau! cela signifie que la création de la table compartimentée ne suffit pas pour que la table soit réellement répartie dans le nombre de compartiments spécifié, car Hive ne l'applique pas à moins que Hive.enforce.bucketing soit défini sur true (ce qui signifie que le nombre de compartiments est défini par quantité de réducteurs dans la dernière étape de la requête en insérant des données dans la table).
En ce qui concerne les performances, veuillez noter que lorsqu’un bucketjoin a tâche unique lit le tableau "plus petit" dans le cache distribué avant que les mappeurs ne l’accèdent et ne se joignent - Cette étape serait probablement très très longue et inefficace lorsque votre table a environ 100 m de lignes!
Ensuite, la jointure sera faite comme dans une jointure normale faite dans les réducteurs.

Pour effectuer un SMBJ, les deux tables doivent avoir exactement la même quantité de compartiments, sur les mêmes colonnes et triées par ces colonnes, en plus de définir Hive.optimize.bucketmapjoin.sortedmerge sur true.
Comme dans l'optimisation précédente, Hive n'applique pas le compartiment ni le tri mais suppose que vous vous êtes assuré que les tables sont réellement classées et classées (non seulement par définition, mais en définissant Hive.enforce.sorting ou en triant manuellement les données tout en les insérant). ) - Ceci est très important car il {peut conduire à des résultats erronés dans les deux cas}.
Du côté de la performance, cette optimisation est bien plus efficace pour les raisons suivantes:

  1. Chaque mappeur lit les deux compartiments et il n'y a pas de conflit de tâches unique pour le chargement de cache distribué
  2. La jointure en cours d'exécution est une jointure de type fusion/tri car les données sont déjà triées, ce qui est très efficace. 

Veuillez noter les considérations suivantes:

  • dans les deux cas set Hive.input.format=org.Apache.hadoop.Hive.ql.io.BucketizedHiveInputFormat;
    devrait être exécuté
  • dans les deux cas, un /*+ MAPJOIN(b) */ devrait être appliqué dans la requête (juste après la select et où b est la plus petite table)
  • Combien de seaux?
    Cela devrait être considéré sous cet angle: la considération doit être appliquée strictement à la table la plus grande car elle a plus d’impact dans cette direction et la configuration sera appliquée à la table la plus petite. Je pense qu'en règle générale, chaque seau devrait contenir entre 1 et 3 blocs, probablement quelque part près de 2 blocs. Par conséquent, si votre taille de bloc est de 256 Mo, il me semble raisonnable d’avoir environ 512 Mo de données dans chaque compartiment de la table la plus grande, ce qui en fait un simple problème de division.

N'oubliez pas non plus que ces optimisations ne garantiront pas toujours un temps d'interrogation plus rapide.
Disons que vous choisissez de faire un SMBJ, cela ajoute le coût du tri de 2 tables avant d'exécuter la jointure - de sorte que plus vous exécuterez votre requête, moins vous "payerez" pour cette étape de tri. 

Parfois, une simple jointure donnera les meilleures performances et aucune des optimisations ci-dessus ne vous aidera. Par conséquent, vous devrez optimiser le processus de jointure habituel, que ce soit au niveau application/logique ou en ajustant les paramètres MapReduce/Hive tels que l'utilisation de la mémoire/le parallélisme, etc.

15
dimamah

Je ne pense pas que ce soit un critère essentiel "la quantité de compartiments dans une table doit être un multiple de la quantité de compartiments de l'autre table" pour la jointure de seau de carte. Nous pouvons également avoir le même nombre de compartiments.

0
user3340714