Problème de performance avec SAS/ACCESS to Hadoop ? Quelques pistes et solutions !
Lors de l’utilisation de SAS/ACCESS to Hadoop, vous pouvez être confronté à des problèmes de performances lors de l’extraction de données.
Aussi, vous pouvez constater que la même requête exécuté en SQL Explicit Pass-Through a un temps de réponse correct mais que les performances en Implicit sont mauvaises.
Première chose à faire, lire mes deux articles Optimisez les performances de traitement de vos données avec SAS/ACCESS® et Comprendre les différents types de connexion lors de la définition d’une bibliothèque d’accès à une base de données.
Ensuite, vérifiez que vous ne travaillez pas avec des vues Hadoop (http://support.sas.com/kb/54/924.html)
Enfin, comme dans tout problème de performance la première étape consiste à positionner l’option sastrace pour voir le contenu de la « boite noire » et mettre le doigt sur l’origine du problème.
Je vous propose un exemple de code « prêt à l’emploi » permettant d’analyser correctement le problème :
1 |
<strong>options sastrace=",,,dbsa" sastraceloc=saslog nostsuffix;</strong><br><br><strong>proc printto log="/tmp/explicit.log" new; </strong><br><strong>run; </strong><br><strong>proc sql;</strong><br><strong>connect to hadoop (user=xxxx server=xxxx password=xxxx database=xxxx select * from connection to hadoop ( select * From table) ; quit;</strong><br><strong>proc printto log="/tmp/implicit.log" new; run ;</strong><br><br><strong>options sastrace=",,,dbsa" sastraceloc=saslog nostsuffix;</strong><br><strong>libname pdd2 hadoop user=xxxx server=xxxx password=xxx SCHEMA=xxx;</strong><br><strong> PROC SQL; </strong><br><strong>select * From table</strong><br><strong>;Quit; </strong> |
Revenons maintenant à notre problème de performance Hadoop. Avant de se jeter dans une analyse poussée, prenons un exemple concret. Nous interrogeons une table Hadoop de plusieurs millions de lignes. Nous constatons que la requête s’exécute en 18 minutes alors que, si je l’exécute directement sur mon cluster Hadoop, via hive, celle-ci ne dure que 1 minute . Vous constatez que ces lenteurs ne se produisent pas sur toute vos tables, qu’elles soient volumineuse ou pas.
Regardons tout ça en détail.
Le code SAS est le suivant :
proc sql; create table visite_1828759 as select * from hivelib.site_we_blog where id_visite = '1828759'; quit;
Nous soumettons également cette requête en SQL Explicit Pass-Through afin de comparer le temps d’exécution.
Maintenant que nous avons tous les éléments et poser le cas, étudions de façon plus précise ce que pourrait poser problème.
Comme vu en introduction, positionnons l’option SASTRACE et jetons un coup d’œil aux log SAS :
HADOOP_1: Executed: on connection 0 USE default
HADOOP_2: Prepared: on connection 0 SHOW TABLES 'site_we_blog' HADOOP_3: Prepared: on connection 0 DESCRIBE FORMATTED site_we_blog HADOOP_4: Prepared: on connection 0 SELECT * FROM 'site_we_blog' HADOOP_5: Prepared: on connection 0 SELECT 'site_we_blog'.id_visite,'site_we_blog'.cookie_id,'site_we_blog'.date,'site_we_blog'.lang FROM 'site_we_blog' WHERE ( 'id_visite' = '1828759' )
La première chose que l’on constate est la presence de deux requetes sql :
- Une première requete préliminaire :
SELECT * FROM 'site_we_blog'
- Puis notre requête que j’appelle fonctionnelle, celle qui nous intéresse :
SELECT 'site_we_blog'.id_visite,'site_we_blog'.cookie_id,'site_we_blog'.date,'site_we_blog'.lang FROM 'site_we_blog' WHERE ( 'id_visite' = '1828759' )
L’analyse côté Hadoop, dans le cluster, confirme l’exécution de ces deux requêtes. Nous avons ainsi 2 tâches MapReduce. Un pour la requête préliminaire et une seconde pour notre requête fonctionnelle.
La requête préliminaire n’est présente qu’en SQL implicit pass-through. Elle sert à déterminer la structure de la table et à récupérer des informations sur celle-ci. Ce mécanisme n’est pas limité au moteur SAS/ACCESS interface to Hadoop mais existe dans tous les moteurs SAS/ACCESS (Oracle, SQL Server, Netezza…)
C’est bien cette requête qui pénalise le temps de traitement.
Pour certaines requêtes simples, l'exécution d'une tâche de MapReduce implique beaucoup de charges. Ce charge peut être évité en utilisant hive.fetch.task.conversion.
Cette propriété Hadoop précise que certaines requêtes remplissant des conditions appropriées soient converties en une seule tâche FETCH, ce qui minimise de façon très important cette latence et le temps exécution.
On ne peut pas faire plus simple que la requête SELECT * FROM 'site_we_blog', elle est donc un bon candidat à cette tâche FETCH plutôt qu’à l’exécution d’une tâche MapReduce plus coûteuse.
Comment mettre en place de fetch.task ?
La propriété Hadoop hive.fetch.task.conversion ne s’utilise pas tout seule mais fonctionne main dans la main avec la propriété hive.fetch.task.conversion.threshold.
hive.fetch.task.conversion peut prendre trois valeurs distinctes :
- none
- minimal
- more
Source : https://cwiki.apache.org/confluence/display/Hive/Configuration%20Properties
La propriété hive.fetch.task.conversion.threshold indique que si la taille de table cible est supérieur à la valeur indiqué, hive utilise MapReduce plutôt que FetchTask pour traiter la requête. Avec une valeur à 1 go, si la table a une taille inférieur à 1 GO et que la requête répond bien à certains critères (une seule data source, pas de sous requete, pas d'agrégations ou de distinct, pas de JOIN) alors FetchTask est exécuté.
En positionnant cette propriété à -1, il n'y as plus de notion de limite de taille; si la requête est "simple" alors Hive utilise FetchTask; Ce qui est le cas avec la requête préliminaire "select * from *"; Seule la requête suivante, toujours sur cette même table est exécuté avec MapReduc, si elle est plus complexe que la préliminaire.
Comment positionner ces propriétés ?
Comme vous l’avez compris il s’agit de propriété Hadoop permettant une customisation (tuning) des valeurs par défaut.
Il est possible de modifier le paramétrage de votre cluster Hadoop, mais si vous souhaitez n’impacter que vos traitement SAS, il est possible de surcharger la configuration Hadoop lors de la soumission de votre libname.
La syntaxe est alors la suivante :
libname hivelib hadoop user=xxxxserver=xxxx password=xxx SCHEMA=xxx PROPERTIES='hive.fetch.task.conversion=minimal;hive.fetch.task.conversion.threshold=-1' ;
Choisir les valeurs adéquates :
Lors de mes tests nous avons vu qu'avec la propriété à more, la proc SQL n'est plus pénalisé par la requête préliminaire servant au describe mais la requête fonctionnelle, celle, je le rappelle, définit dans la proc SQL se trouve pénalisé car exécuté via un FETCH task et non pas une tâche MapReduce.
En positionnant cette propriété à minimal, on réduit le scope d'utilisation du fetch task au détriment du MapReduce:
- none: Désactive l’utilisation de hive.fetch.task.conversion
- minimal: Utilise hive.fetch.task.conversion avec des requêtes SELECT *, FILTER sur des colonnes partionnées (WHERE and HAVING clauses), LIMIT uniquement
- more: Utilise hive.fetch.task.conversion avec des SELECT, FILTER, LIMIT uniquement
Aussi, pour une proc SQL, le meilleur paramétrage est donc minimal. Avec cette propriété, le SELECT * FROM préliminaire est exécuté via la méthode "hive.fetch.task.conversion". Puis la seconde requête est bien exécutée avec une tâche MapReduce.
Mise à jour du 02 Mai 2016 : Une problem Note SAS vient d'être publié sur ce problème et préconisant les paramétrs Hadoop expliqués en détail dans cette article.
Retrouvez la sur le site de SAS : Problem Note 57776: Queries run against a large Hive table might be slow