Cet article est la traduction d’un article de Greg Rahn publié sur son blog. L’article original en anglais est: The Core Performance Fundamentals Of Oracle Data Warehousing – Set Processing vs Row Processing. Cet article fait partie d’une série sur les principes fondamentaux des datawarehouse, mais s’applique à tous les traitements de type batch.
Durant 6 ans à faire des Proof Of Concept et des Benchmarks sur des datawarehouse pour les clients, il y a un domaine qui s’est toujours montré problématique: les traitements par lots (batch). La plupart du temps, ces batchs prennent la forme de procédures et packages PL/SQL, qui font du chargement de donnée, de la transformation, du traitement, ou quelque chose de similaire.
La raison pour laquelle c’est souvent problématique, c’est que les développeurs y ont codé en dur la lenteur du traitement. Je suis certain que les développeurs ne savaient pas qu’ils faisaient cela, lorsqu’ils ont codé leur PL/SQL, mais en tout cas, c’est ce qui est arrivé.
Alors comment ont-ils codé ‘en dur’ cette lenteur en PL/SQL ?
En général, c’est parce que au lieu d’avoir lu les spécifications métiers en étudiant l’état ‘avant’ et ‘après’ des données, puis d’en avoir déterminé la manière la plus efficace de faire ces modifications de données, les développeurs PL/SQL ont fait une traduction littérale de chaque règle/exigence lues dans les specs, une par une. On devine cela lorsqu’on voit du code qui parcours un curseur ligne par ligne, mais aussi dans du PL/SQL qui ne fait qu’exécuter une série de requêtes SQL souvent mal concues.
Etude de cas de la lenteur codée ‘en dur’
La suite est basée sur une histoire vécue. Seuls les noms ont été modifiés pour protéger les innocents.
Voici un extrait de pseudo-code que j’ai rencontré dans un POC:
{truncate de toutes les tables intermédiaires} insert into temp1 select * from t1 where create_date = hier; insert into temp1 select * from t2 where create_date = hier; insert into temp1 select * from t3 where create_date = hier; insert into temp2 select * from temp1 where {conditions}; insert into target_table select * from temp2; pour chacune des 20 colonnes loop update target_table t set t.column_name = (select column_name from t4 where t.id=t4.id ) where i.column_name is null end loop update target_table t set {liste de 50 columns} = select {50 colonnes} from t5 where t.id=t5.id;
Je m’arrête ici car la suite risquerait de vous faire pleurer.
J’hésite un peu à poser la question, mais n’est-ce pas évident ce qu’il y a de pas correct dans ce traitement ?
Voici les principales raisons que je vois à cette inefficacité:
- Pourquoi insérer toutes les données dans temp1, et ne les filtrer que lorsqu’on remplit temp2 ? Si vous n’avez jamais entendu le conseil ‘filtrez les données au plus tôt’ alors vous avez des devoirs à faire.
- Pourquoi publier dans la table cible, puis faire 20 update d’une seule colonne chacun, suivi d’un update de 50 colonnes ? Meilleure question: pourquoi faire des update de masse ? Les update (et delete) de masse sont à proscrire, il faut les éviter à tout prix.
Et alors, comme beaucoup de client qui font un POC (preuve de concept) sur une machine Exadata, ils n’ont pas du tout envie de modifier leur code, ils veulent juste voir quelle performance la plateforme Exadata peur leur délivrer. Et pour leur plus grand bonheur, ils ont réduit la durée du traitement de 2.5 jours (batch de week-end qui commence le vendredi après midi et n’est toujours pas terminé le lundi matin) à 10 heures, gagnant ainsi plus de 2 jours. Maintenant, le batch peut planter et ils auront le temps de le relancer avant l’ouverture aux utilisateurs du lundi matin. Eh, je suppose que je serais aussi très exité si je gagnait 24 heures sur un traitement de 38 heures. Mais ce n’est pas le cas lorsque je suis un ingénieur de performance de base de données qui sait qu’il y a encore plus de performance à gagner.
Comme je n’était pas satisfait par cela, j’ai pris sur moi de prouver que de re-designer le code peut avoir un retour très intéressant sur la plateforme Exadata, et j’ai codé complètement un flux de donnée qui travaille de manière ensembliste, avec juste une poignée de requêtes SQL (et pas de PL/SQL). Le résultat: le traitement d’une semaine complète de données (plusieurs centaines de millions de lignes) prend maintenant 12 minutes. C’est exact: 7 jours de données nettoyées, transformées, enrichies et publiées en 12 minutes seulement.
Lorsque j’ai annoncé la nouvelle au client, qu’il était possible de charger une semaine de données en seulement 12 minutes, ils étaient très excités, c’est le moins que l’on puisse dire. En fait, il y en a un qui a même dit, un peu hors du contexte, que maintenant une seule journée de données pourrait être chargées en 2 minutes, et que cela donnerait un autre niveau de fraîcheur aux données, ce qui permettrait au métier de prendre des décision meilleures et plus rapides, grâce aux données actualisées plus souvent. Ma réponse: CQFD ! Le client voit maintenant ce qu’il est possible de faire avec Exadata, et qui était impossible avant.
C’est pas obligatoire, mais c’est souhaitable
Je ne vais pas changer ma casquette d’ingénieur base de données en vendeur de produit, pas maintenant, et probablement jamais, mais c’est le réalité telle qu’elle existe. Les boites informatiques ont démarré avec des faibles volumes de données, et une logique de programmation sur des petits volumes, et cela a fonctionné un certain temps. Pourquoi ? Parce que le traitement peu efficace d’un faible volume de données est seulement un peu inefficace Mais la même logique de développement sur un gros volume de données devient alors très inefficace.
C’est pourquoi j’ai déjà dit: pour exploiter à fond la plateforme Exadata (ou n’importe quelle plateforme actuelle), il faut changer le code. Ne me faites pas dire ce que je n’ai pas dit: je ne dis pas qu’il est nécessaire revoir le code de l’application pour Exadata. Je dis que vous pouvez choisir de revoir le code pour Exadata parce que l’application n’est tout simplement pas conçue pour profiter du traitement massivement parallèle que Exadata permet. Il est temps que les décisions de design de l’application soient basées sur les technologies d’aujourd’hui, et non sur celles sur lesquelles l’application a été faite. Avance rapide vers aujourd’hui.
Bonne analyse, seulement pour mettre en application ce dont vous parlez dans votre dernier paragraphe, une évolution de mentalités sera nécessaire.