6.1
Lutilisation dobjets doptimisation est un lment qui, de manire gnrale, amliore les performances trs significativement. Cependant, la faon dcrire votre requte peut, elle aussi, dans certains cas, avoir un impact trs significatif. Ltape de transformation de requte (voir ci-aprs) est de plus en plus performante sur Oracle et SQL Server. Elle donne de trs bons rsultats, ce qui rend inutiles certaines recommandations dans le cas gnral. Cependant, sur MySQL ou sur des requtes compliques, o on peut considrer que le SGBDR narriverait pas faire certaines transformations tout seul, lutilisation des critures les plus performantes permettra damliorer les rsultats. Nous allons faire quelques comparaisons dcritures qui intgreront parfois un hint (voir Chapitre7, section7.1, "Utilisation des hints sous Oracle") afin dempcher certaines transformations. De faon gnrale, il faut privilgier les jointures. Le principe du modle relationnel tant que, lors de linterrogation, des jointures seront faites, on peut supposer que les diteurs de SGBDR ont concentr leurs efforts l-dessus. Cependant, sous MySQL avec le moteur MyISAM, les sous-requtes sont souvent plus performantes.
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
154
tudeetoptimisationdesrequtes
Axe 2
6.1.1
Transformation de requtes
Ltape de transformation de requtes (Query Transformation) est partie intgrante du processus de traitement des requtes. Cest une des fonctions de loptimiseur CBO. Cette tape consiste transformer la requte soumise en une requte quivalente afin de la rendre plus performante. Cela va, au-del, de choisir le meilleur chemin dexcution. La transformation de requtes peut, par exemple, dcider de transformer une sous-requte en jointure. Vous pouvez influer sur cette tape au moyen des hints, mais cest rarement ncessaire. Les principales transformations effectues par le CBO sont:
Subquery unesting. Cette transformation consiste transformer des sousrequtes en jointures. Suppression dlments inutiles (certaines jointures, des colonnes slectionnes dans les sous-requtes). Predicate push. Cette transformation consiste dupliquer certains prdicats dans les sous-vues et les sous-requtes. View merging. Intgre lexcution des vues la requte principale. Query Rewriting. Utilisation des vues matrialises (voir Chapitre5, section5.3.5, "Les vues matrialises"). Or Expansion. Transforme des conditions OR en plusieurs requtes fusionnes par une sorte d"Union ALL".
Il ny a rien de particulier faire pour bnficier du Query Rewriting. Connatre lexistence de ce mcanisme vous aidera comprendre pourquoi il arrive parfois quun plan dexcution ne ressemble vraiment pas votre requte. Cette fonction est plus volue sous Oracle et SQL Server que sous MySQL.
INFO Au cours de ce chapitre, nous prsentons des variations de notation qui peuvent avoir un effet sur les performances. L'optimiseur tant assez performant, il transforme automatiquement les requtes soumises en la version la plus performante. Nous recourrons des hints pour empcher ces transformations afin de comparer les notations. L'utilisation de hint n'est nullement une recommandation, nous le mentionnons titre indicatif de faon que, si vous excutez ces requtes, vous puissiez reproduire les mmes rsultats que ceux prsents ici. Comme cela sera expliqu au Chapitre7, section7.1, "Utilisation des hints sous Oracle", il faut les utiliser en dernier recours et seulement en toute connaissance de cause.
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
Chapitre 6
6.1.2
IN versus jointure
Selon que lcriture dune requte se fasse avec une sous-requte et loprateur IN ou avec une jointure, limpact sera diffrent. Pour forcer le parcours des tables, et non seulement celui des index, nous utilisons dans la requte des champs non indexs (Quantit et Editeur).
Listing6.1: Requte utilisant une jointure
select sum(CL.quantite) from cmd_lignes CL,livres L where CL.nolivre = L.nolivre and L.editeur='Pearson'
Nous allons tester ces requtes avec et sans index sur la colonne Nolivre de la table cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);
Sans index MyISAM Temps Key_read_requests InnoDB Temps Reads Jointure 12,89s 5242448 Sous-requte 13,53s 5243442 0,01s 158
InnoDB crant automatiquement un index dans la table fille de la Foreign Key, il y a forcement un index sur la colonne Nolivre de la table cmd_lignes. La cration de lindex est donc sans effets.
Sans index Oracle Temps Consistent Gets Jointure 6,15s 8336 Sous-requte 8,75s 4891735 0,01s 807 Avec index Jointure Sous-requte 0,60s 12175
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
156
tudeetoptimisationdesrequtes
Axe 2
Pour viter la transformation de la version utilisant une sous-requte en version jointure par le mcanisme de transformation de requte, nous devons utiliser le hint /*+no_unnest*/ et pour forcer lusage de lindex, le hint /*+ index (CL IS_cmd_ lignes)*/. Si nous ne mettons aucun hint, loptimiseur choisit systmatiquement de faire une jointure sans utiliser lindex.
Sans index MS SQL Server Temps Estimated Cost Jointure 0,625s 35,27 Avec index Sous-requte 0,046s 60,44
Afin de comparer les rsultats entre les bases, nous dsactivons le paralllisme sous SQL Server au moyen du code option (MAXDOP 1). Nous navons pas trouv de combinaisons de hints permettant de forcer lutilisation dune sous-requte tant quil ny a pas dindex. Une fois lindex plac, nous avons d contraindre son utilisation avec le hint WITH (INDEX(IS_CMD_LIGNES)). En revanche, nous navons pas russi forcer une jointure pertinente utilisant cet index. Conclusion : Oracle et SQL Server, grce leur capacit de transformation de requte volue, excutent les requtes de la mme faon quelle que soit la notation employe, si aucun hint nest utilis. Lorsque la transformation nest pas effectue, on constate que la solution base de jointure est gnralement plus performante. Au cas o le SGBDR narriverait pas faire la transformation, il est prfrable de prendre lhabitude dcrire des jointures plutt que des sous-requtes.
6.1.3 Sous-requtes versus anti-jointures
Une anti-jointure est une jointure ouverte pour laquelle vous ne slectionnez que les valeurs non jointes. Les requtes ci-dessous, permettent de slectionner les livres qui nont jamais t commands.
Listing6.3: Version Exists
select * from livres L where not exists (select 1 from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE)
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
Chapitre 6
Nous allons tester ces requtes avec et sans index sur la colonne Nolivre de la table cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);
Sans index MyISAM Temps Key_read_requests Anti-jointure 10,11s 29850364 Sous-requte 10,11s 29850364
InnoDB crant automatiquement un index dans la table fille de la Foreign Key, il y a forcment un index sur la colonne Nolivre de la table cmd_lignes. La cration de lindex est donc sans effets.
Sans index Oracle Temps Consistent Gets Anti-jointure 0,340s 6976 Sous-requte 2,282s 113419 0,375s 5522 Avec index Anti-jointure Sous-requte 0,031s 8909
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
158
tudeetoptimisationdesrequtes
Axe 2
Quelle que soit la notation utilise, la requte est transforme en anti-jointure. Les rsultats sont mitigs sous Oracle. Malheureusement, ltape de transformation de requte nopte pas toujours pour la meilleure option. Loptimiseur choisit systmatiquement lanti-jointure qui est, en effet, gnralement la meilleure option. Ici, la table livre est bien plus petite que la table cmd_lignes, dans ce cas, si un index est prsent sur la table fille de la Nested Loop, la solution utilisant une sous-requte est la meilleure. Il faut donc utiliser un hint pour forcer ce choix. Sur les autres SGBDR, ltape de transformation ne laisse pas le choix, les deux critures se valent.
6.1.4 Exists versus Count
Nous allons tester ces requtes avec un index sur la colonne Nolivre de la table cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);
MyISAM Temps Key_read_requests InnoDB Temps Reads Oracle Temps Consistent Gets
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
Chapitre 6
Exists 0,062s
Count 0,062s
Oracle et SQL Server considrent ces deux critures quivalentes, alors que MySQL les voit bien diffrentes. On suppose que dans la version utilisant COUNT(*), il dnombre toutes les occurrences pour tester lgalit 0, alors que la version utilisant EXISTS sarrte la premire occurrence trouve.
6.1.5 Exists versus IN
IN
Nous allons tester ces requtes avec un index sur la colonne Nolivre de la table cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);
MyISAM Temps Key_read_requests InnoDB Temps Reads Oracle Temps Consistent Gets
Exists 15,340s 5242448 Exists 8,200s 5212684 Exists 8,594s 4891735 Forage index 4,234s 2462067
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
160
tudeetoptimisationdesrequtes
Axe 2
Loptimiseur Oracle dcide de ne pas utiliser lindex. Si nous forons son usage laide du hint /*+ index(cl is_cmd_lignes)*/, nous notons une amlioration des performances. Nous utilisons le hint /*+NO_QUERY_TRANSFORMATION*/ pour empcher la transformation des sous-requtes en anti-jointures qui ont un temps de rponse de lordre 0,34seconde.
MS SQL Server Temps In ou Exists 0,625s Forage index 0,046s
WITH
Comme sous Oracle, nous devons forcer lutilisation de lindex avec le hint
(INDEX(IS_CMD_LIGNES)).
Oracle et SQL Server considrent ces deux critures quivalentes, alors que MySQL les voit diffrentes, lavantage tant loprateur IN sur les deux moteurs de MySQL.
6.1.6 Clause Exists * versus constante
On enseigne, depuis des annes, que lorsquon emploie des sous-requtes corrles utilisant loprateur EXISTS, il faut retourner une constante (numrique ou texte) dans la clause SELECT de la sous-requte. Est-ce seulement pour des raisons de clart ou aussi de performances?
Listing6.9: Version Exists constante
select * from livres L where not exists (select 1 from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE);
Nous ne documentons pas le dtail des rsultats. On constate que les colonnes retournes dans la sous-requte nont aucun impact sur les performances, alors quelles en avaient sur danciennes versions dOracle. Cette pratique de mettre une constante garde cependant tout son sens du point de vue de la comprhension. Parfois, certains dveloppeurs mettent ici un champ particulier laissant ainsi croire quil y aurait une sorte de lien possible avec ce champ.
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
Chapitre 6
6.1.7
Nous choisissons ici une requte qui empche dappliquer le prdicat de la table cmd directement la table cmd_lignes et qui ne retourne que 173lignes. En changeant la date, nous testons une autre version qui ramne beaucoup plus de lignes (132141).
Petite requte MyISAM Temps Reads Jointure 5,890s 4873397 Petite requte InnoDB Temps Reads Jointure 0,670s 1001449 Expression sous-requte 0,670s 1000175 Expression sous-requte 0,060s 3316 Expression sous-requte 0,109s Expression sous-requte 0,160s 736 Grosse requte Jointure 6,810s 4873397 Expression sous-requte 1,950s 564513 Expression sous-requte 4,030s 1632143 Expression sous-requte 1,51s 117464 Expression sous-requte 0,953s
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro
162
tudeetoptimisationdesrequtes
Axe 2
Avec des requtes ramenant peu de lignes, l'avantage est aux expressions sousrequtes. Avec des requtes ramenant beaucoup de lignes, l'avantage passe la version jointure, sauf pour le moteur MyISAM qui a l'air plus l'aise avec l'utilisation systmatique d'expressions sous-requtes. Seul l'optimiseur de SQL Server adapte automatiquement son plan d'excution la solution la plus efficace, indpendamment de l'criture. Nous avons donc utilis les hints loop join et merge join pour effectuer ces tests.
6.1.8 Agrgats: Having versus Where
Quand cest possible, les conditions doivent tre places dans la clause WHERE plutt que dans la clause HAVING qui est value aprs les oprations dagrgation. Cela permet de profiter dventuels index et rduit le volume traiter durant lopration dagrgation.
Listing6.13: Requte utilisant Having (incorrecte)
select pays,ville,count(*) from clients t group by pays,ville having pays='France'
MyISAM Temps Reads InnoDB Temps Reads Oracle Temps Consistent Gets MS SQL Server Temps
Having 0,080s 91109 Having 0,090s 91132 Having 0,047s 624 Having 0,046s
Where 0,030s 45950 Where 0,050s 45960 Where 0,015s 624 Where 0,046s
2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro