| 1 |
<!-- |
|---|
| 2 |
$Header: /var/lib/cvs/pgsql-fr/sgml/arch-dev.sgml,v 1.8.2.3 2005/07/15 06:33:35 guillaume Exp $ |
|---|
| 3 |
--> |
|---|
| 4 |
|
|---|
| 5 |
<chapter id="overview"> |
|---|
| 6 |
<title>Présentation des mécanismes internes de PostgreSQL</title> |
|---|
| 7 |
|
|---|
| 8 |
<note> |
|---|
| 9 |
<title>Auteur</title> |
|---|
| 10 |
<para> |
|---|
| 11 |
Ce chapitre vient originellement de <xref linkend="SIM98">, la thèse de |
|---|
| 12 |
Stefan Simkovics préparée à l'université de technologie de Vienne sous la |
|---|
| 13 |
direction du Dr. Georg Gottlob et de son assistante Katrin Seyr. |
|---|
| 14 |
</para> |
|---|
| 15 |
</note> |
|---|
| 16 |
|
|---|
| 17 |
<para> |
|---|
| 18 |
Ce chapitre présente la structure interne du serveur |
|---|
| 19 |
<productname>PostgreSQL</productname>. Après avoir lu les sections suivantes, |
|---|
| 20 |
vous devriez avoir une idée sur la façon dont une requête est exécutée. Ce |
|---|
| 21 |
chapitre n'a pas pour but de donner une description détaillée des opérations |
|---|
| 22 |
internes de <productname>PostgreSQL</productname> car un tel document |
|---|
| 23 |
serait énorme. À la place, ce chapitre a pour but d'aider le lecteur à |
|---|
| 24 |
comprendre la suite générale des opérations arrivant au niveau serveur à |
|---|
| 25 |
partir du moment où une requête est reçue jusqu'au moment où les résultats |
|---|
| 26 |
sont renvoyés au client. |
|---|
| 27 |
</para> |
|---|
| 28 |
|
|---|
| 29 |
<sect1 id="query-path"> |
|---|
| 30 |
<title>Chemin d'une requête</title> |
|---|
| 31 |
|
|---|
| 32 |
<para> |
|---|
| 33 |
Ici, nous allons donner un rapide aperçu des étapes qu'une requête doit |
|---|
| 34 |
franchir pour obtenir un résultat. |
|---|
| 35 |
</para> |
|---|
| 36 |
|
|---|
| 37 |
<procedure> |
|---|
| 38 |
<step> |
|---|
| 39 |
<para> |
|---|
| 40 |
Une connexion doit être établie à partir d'une application au serveur |
|---|
| 41 |
<productname>PostgreSQL</productname>. L'application transmet une requête |
|---|
| 42 |
au serveur et attend de recevoir les résultats renvoyés par le serveur. |
|---|
| 43 |
</para> |
|---|
| 44 |
</step> |
|---|
| 45 |
|
|---|
| 46 |
<step> |
|---|
| 47 |
<para> |
|---|
| 48 |
L'<firstterm>étape de l'analyse</firstterm> vérifie la requête transmise |
|---|
| 49 |
par l'application au niveau de la syntaxe et crée un <firstterm>arbre |
|---|
| 50 |
représentant la requête</firstterm>. |
|---|
| 51 |
</para> |
|---|
| 52 |
</step> |
|---|
| 53 |
|
|---|
| 54 |
<step> |
|---|
| 55 |
<para> |
|---|
| 56 |
Le <firstterm>système de réécriture</firstterm> prend l'arbre représentant |
|---|
| 57 |
la requête et cherche les <firstterm>règles</firstterm> (stockées dans les |
|---|
| 58 |
<firstterm>catalogues système</firstterm>) à appliquer à l'arbre de la |
|---|
| 59 |
requête. Il exécute les transformations données dans les <firstterm>corps |
|---|
| 60 |
des règles</firstterm>. Une application du système de réécriture est la |
|---|
| 61 |
réalisation des <firstterm>vues</firstterm>. |
|---|
| 62 |
</para> |
|---|
| 63 |
|
|---|
| 64 |
<para> |
|---|
| 65 |
À chaque fois qu'une requête comprenant une vue (c'est-à-dire une |
|---|
| 66 |
<firstterm>table virtuelle</firstterm>) est exécutée, le système de |
|---|
| 67 |
réécriture modifie la requête de l'utilisateur par la |
|---|
| 68 |
<firstterm>définition de la vue</firstterm>, c'est-à-dire une requête qui |
|---|
| 69 |
accède aux <firstterm>tables de base</firstterm>. |
|---|
| 70 |
</para> |
|---|
| 71 |
</step> |
|---|
| 72 |
|
|---|
| 73 |
<step> |
|---|
| 74 |
<para> |
|---|
| 75 |
Le <firstterm>planificateur/optimiseur</firstterm> prend l'arbre |
|---|
| 76 |
(réécrit) de la requête et crée un <firstterm>plan |
|---|
| 77 |
d'exécution</firstterm> qui sera l'entrée de |
|---|
| 78 |
l'<firstterm>exécuteur</firstterm>. |
|---|
| 79 |
</para> |
|---|
| 80 |
|
|---|
| 81 |
<para> |
|---|
| 82 |
Il le fait tout d'abord en créant tous les <firstterm>chemins</firstterm> |
|---|
| 83 |
possibles menant au même résultat. Par exemple, s'il existe un index sur |
|---|
| 84 |
une relation à parcourir, il existe deux chemins pour le parcours. Une |
|---|
| 85 |
possibilité est un parcours simple séquentiel et l'autre possibilité est |
|---|
| 86 |
d'utiliser l'index. Ensuite, le coût pour l'exécution de chaque plan est |
|---|
| 87 |
estimé et le plan le moins coûteux est choisi et renvoyé. |
|---|
| 88 |
</para> |
|---|
| 89 |
</step> |
|---|
| 90 |
|
|---|
| 91 |
<step> |
|---|
| 92 |
<para> |
|---|
| 93 |
L'exécuteur passe récursivement dans l'<firstterm>arbre constitué par le |
|---|
| 94 |
plan</firstterm> et retrouve les lignes suivant la façon |
|---|
| 95 |
représentée par le plan. L'exécuteur fait usage du <firstterm>système de |
|---|
| 96 |
stockage</firstterm> lors du parcours des relations, exécute les |
|---|
| 97 |
<firstterm>tris</firstterm> et <firstterm>jointures</firstterm>, évalue |
|---|
| 98 |
les <firstterm>qualifications</firstterm> et finalement renvoie les |
|---|
| 99 |
lignes en question. |
|---|
| 100 |
</para> |
|---|
| 101 |
</step> |
|---|
| 102 |
</procedure> |
|---|
| 103 |
|
|---|
| 104 |
<para> |
|---|
| 105 |
Dans les sections suivantes, nous allons parler de chacun des éléments |
|---|
| 106 |
discutés ci-dessus avec plus de détails pour donner une meilleure |
|---|
| 107 |
compréhension des structures de données et de contrôle internes de |
|---|
| 108 |
<productname>PostgreSQL</productname>. |
|---|
| 109 |
</para> |
|---|
| 110 |
</sect1> |
|---|
| 111 |
|
|---|
| 112 |
<sect1 id="connect-estab"> |
|---|
| 113 |
<title>Moyens pour établir des connexions</title> |
|---|
| 114 |
|
|---|
| 115 |
<para> |
|---|
| 116 |
<productname>PostgreSQL</productname> est implémenté suivant un modèle |
|---|
| 117 |
client/serveur à chaque <quote>processus par utilisateur</>. Dans ce modèle, il |
|---|
| 118 |
existe un <firstterm>processus client</firstterm> connecté à un seul |
|---|
| 119 |
<firstterm>processus serveur</firstterm>. Comme nous ne savons pas par avance |
|---|
| 120 |
combien de connexions seront établies, nous devons utiliser un |
|---|
| 121 |
<firstterm>processus maître</firstterm> qui lancera un processus serveur à |
|---|
| 122 |
chaque fois qu'une connexion sera demandée. Ce processus maître s'appelle |
|---|
| 123 |
<literal>postmaster</literal> et écoute sur le port TCP/IP spécifié pour des |
|---|
| 124 |
connexions entrantes. À chaque fois qu'une demande pour une connexion est |
|---|
| 125 |
détectée, le processus <literal>postmaster</literal> lance un nouveau |
|---|
| 126 |
processus fils appelé <literal>postgres</literal>. Les tâches du serveur |
|---|
| 127 |
(processus <literal>postgres</literal>) communiquent entre elles en |
|---|
| 128 |
utilisant des <firstterm>sémaphores</firstterm> et de la <firstterm>mémoire |
|---|
| 129 |
partagée</firstterm> pour s'assurer de l'intégrité des données lors d'un |
|---|
| 130 |
accès simultané aux données. |
|---|
| 131 |
</para> |
|---|
| 132 |
|
|---|
| 133 |
<para> |
|---|
| 134 |
Le processus client peut être tout programme comprenant le protocole |
|---|
| 135 |
<productname>PostgreSQL</productname> décrit dans le |
|---|
| 136 |
<xref linkend="protocol">. Beaucoup de clients sont basés sur la |
|---|
| 137 |
bibliothèque C <application>libpq</> mais plusieurs implémentations |
|---|
| 138 |
indépendantes existent, telle que le pilote Java <application>JDBC</>. |
|---|
| 139 |
</para> |
|---|
| 140 |
|
|---|
| 141 |
<para> |
|---|
| 142 |
Une fois la connexion établie, le processus client peut envoyer une requête |
|---|
| 143 |
au serveur (<firstterm>backend</firstterm>). La requête est transmise en |
|---|
| 144 |
texte simple, c'est-à-dire qu'aucune analyse n'a besoin d'être réalisée au |
|---|
| 145 |
niveau de l'<firstterm>interface</firstterm> (client). Le serveur analyse |
|---|
| 146 |
la requête, crée un <firstterm>plan d'exécution</firstterm>, exécute le |
|---|
| 147 |
plan et renvoie les lignes trouvées au client par la connexion établie. |
|---|
| 148 |
</para> |
|---|
| 149 |
</sect1> |
|---|
| 150 |
|
|---|
| 151 |
<sect1 id="parser-stage"> |
|---|
| 152 |
<title>Étape d'analyse</title> |
|---|
| 153 |
|
|---|
| 154 |
<para> |
|---|
| 155 |
L'<firstterm>étape d'analyse</firstterm> consiste en deux parties : |
|---|
| 156 |
|
|---|
| 157 |
<itemizedlist> |
|---|
| 158 |
<listitem> |
|---|
| 159 |
<para> |
|---|
| 160 |
L'<firstterm>analyseur</firstterm> défini dans |
|---|
| 161 |
<filename>gram.y</filename> et <filename>scan.l</filename> est construit |
|---|
| 162 |
en utilisant les outils Unix <application>yacc</application> et |
|---|
| 163 |
<application>lex</application>. |
|---|
| 164 |
</para> |
|---|
| 165 |
</listitem> |
|---|
| 166 |
<listitem> |
|---|
| 167 |
<para> |
|---|
| 168 |
Le <firstterm>processus de transformation</firstterm> fait des |
|---|
| 169 |
modifications et des ajouts aux structures de données renvoyées par |
|---|
| 170 |
l'analyseur. |
|---|
| 171 |
</para> |
|---|
| 172 |
</listitem> |
|---|
| 173 |
</itemizedlist> |
|---|
| 174 |
</para> |
|---|
| 175 |
|
|---|
| 176 |
<sect2> |
|---|
| 177 |
<title>Analyseur</title> |
|---|
| 178 |
|
|---|
| 179 |
<para> |
|---|
| 180 |
L'analyseur doit vérifier la syntaxe valide de la chaîne de la requête |
|---|
| 181 |
(arrivant comme un texte ASCII). Si la syntaxe est correcte, un |
|---|
| 182 |
<firstterm>arbre d'analyse</firstterm> est construit et renvoyé, sinon |
|---|
| 183 |
une erreur est retournée. Les analyseur et vérificateur syntaxiques sont |
|---|
| 184 |
implémentés en utilisant les outils bien connus que sont |
|---|
| 185 |
<application>lex</application> et <application>yacc</application>. |
|---|
| 186 |
</para> |
|---|
| 187 |
|
|---|
| 188 |
<para> |
|---|
| 189 |
L'<firstterm>analyseur lexical</firstterm> est défini dans le fichier |
|---|
| 190 |
<filename>scan.l</filename> et est responsable de la reconnaissance des |
|---|
| 191 |
<firstterm>identificateurs</firstterm>, des <firstterm>mots clés |
|---|
| 192 |
SQL</firstterm>, etc. Pour chaque mot clé ou identificateur trouvé, un |
|---|
| 193 |
<firstterm>jeton</firstterm> est généré et renvoyé à l'analyseur. |
|---|
| 194 |
</para> |
|---|
| 195 |
|
|---|
| 196 |
<para> |
|---|
| 197 |
L'analyseur est défini dans le fichier <filename>gram.y</filename> et |
|---|
| 198 |
consiste en un ensemble de <firstterm>règles de grammaire</firstterm> et |
|---|
| 199 |
en des <firstterm>actions</firstterm> à exécuter lorsqu'une règle est |
|---|
| 200 |
découverte. Le code des actions (qui est en langage C) est utilisé pour |
|---|
| 201 |
construire l'arbre d'analyse. |
|---|
| 202 |
</para> |
|---|
| 203 |
|
|---|
| 204 |
<para> |
|---|
| 205 |
Le fichier <filename>scan.l</filename> est transformé en fichier source C |
|---|
| 206 |
<filename>scan.c</filename> en utilisant le programme |
|---|
| 207 |
<application>lex</application> et <filename>gram.y</filename> est |
|---|
| 208 |
transformé en <filename>gram.c</filename> en utilisant |
|---|
| 209 |
<application>yacc</application>. Après avoir réalisé ces transformations, |
|---|
| 210 |
un compilateur C normal peut être utilisé pour créer l'analyseur. Ne jamais |
|---|
| 211 |
faire de changement aux fichiers C générés car ils seront écrasés la |
|---|
| 212 |
prochaine fois que <application>lex</application> ou |
|---|
| 213 |
<application>yacc</application> seront appelés. |
|---|
| 214 |
|
|---|
| 215 |
<note> |
|---|
| 216 |
<para> |
|---|
| 217 |
Les transformations et compilations mentionnées sont normalement |
|---|
| 218 |
réalisées automatiquement en utilisant les |
|---|
| 219 |
<firstterm>makefiles</firstterm> distribués avec les sources de |
|---|
| 220 |
<productname>PostgreSQL</productname>. |
|---|
| 221 |
</para> |
|---|
| 222 |
</note> |
|---|
| 223 |
</para> |
|---|
| 224 |
|
|---|
| 225 |
<para> |
|---|
| 226 |
Une description détaillée de <application>yacc</application> ou des règles |
|---|
| 227 |
de grammaire données dans <filename>gram.y</filename> serait en dehors du |
|---|
| 228 |
champ de ce document. Il existe de nombreux livres et documentations en |
|---|
| 229 |
relation avec <application>lex</application> et |
|---|
| 230 |
<application>yacc</application>. Vous devez être familier avec |
|---|
| 231 |
<application>yacc</application> avant de commencer à étudier la grammaire |
|---|
| 232 |
donnée dans <filename>gram.y</filename>, sinon vous ne comprendrez rien à |
|---|
| 233 |
ce qui s'y passe. |
|---|
| 234 |
</para> |
|---|
| 235 |
|
|---|
| 236 |
</sect2> |
|---|
| 237 |
|
|---|
| 238 |
<sect2> |
|---|
| 239 |
<title>Processus de transformation</title> |
|---|
| 240 |
|
|---|
| 241 |
<para> |
|---|
| 242 |
L'étape d'analyse crée un arbre d'analyse utilisant seulement les règles |
|---|
| 243 |
fixes de la structure syntaxique de SQL. Il ne fait aucune recherche dans |
|---|
| 244 |
les catalogues système, donc il n'y a aucune possibilité de comprendre |
|---|
| 245 |
la sémantique détaillée des opérations demandées. Après que l'analyseur |
|---|
| 246 |
ait fini, le <firstterm>processus de transformation</firstterm> prend |
|---|
| 247 |
l'arbre résultant de l'analyseur en entrée et réalise l'interprétation |
|---|
| 248 |
sémantique nécessaire pour connaître les tables, fonctions et opérateurs |
|---|
| 249 |
référencés par la requête. La structure de données construite pour |
|---|
| 250 |
représenter cette information est appelé l'<firstterm>arbre de la |
|---|
| 251 |
requête</>. |
|---|
| 252 |
</para> |
|---|
| 253 |
|
|---|
| 254 |
<para> |
|---|
| 255 |
La raison de la séparation de l'analyse brute et de l'analyse sémantique |
|---|
| 256 |
est que les recherches des catalogues système peuvent seulement se faire |
|---|
| 257 |
à l'intérieur d'une transaction, et nous ne voulons pas commencer une |
|---|
| 258 |
transaction immédiatement après avoir reçu une requête. L'analyse brute |
|---|
| 259 |
est suffisante pour identifier les commandes de contrôle des transactions |
|---|
| 260 |
(<command>BEGIN</>, <command>ROLLBACK</>, etc) et elles peuvent être |
|---|
| 261 |
correctement exécutées sans plus d'analyse. Une fois que nous savons que |
|---|
| 262 |
nous gérons une vraie requête (telle que <command>SELECT</> ou |
|---|
| 263 |
<command>UPDATE</>), nous pouvons commencer une transaction si nous n'y |
|---|
| 264 |
sommes pas déjà. C'est seulement maintenant que le processus de |
|---|
| 265 |
transformation peut être appelé. |
|---|
| 266 |
</para> |
|---|
| 267 |
|
|---|
| 268 |
<para> |
|---|
| 269 |
La plupart du temps, l'arbre d'une requête créée par le processus de |
|---|
| 270 |
transformation est structurellement similaire à l'arbre d'analyse brute |
|---|
| 271 |
mais il existe beaucoup de différences dans le détail. Par exemple, un |
|---|
| 272 |
nœud <structname>FuncCall</> dans l'arbre d'analyse représente |
|---|
| 273 |
quelque chose qui ressemble syntaxiquement à l'appel d'une fonction. Il |
|---|
| 274 |
peut être transformé soit en nœud <structname>FuncExpr</> soit en |
|---|
| 275 |
nœud <structname>Aggref</> suivant que le nom référencé est une |
|---|
| 276 |
fonction ordinaire ou une fonction d'agrégat. De même, des informations sur |
|---|
| 277 |
les types de données réels des colonnes et des résultats sont ajoutées à |
|---|
| 278 |
l'arbre de la requête. |
|---|
| 279 |
</para> |
|---|
| 280 |
</sect2> |
|---|
| 281 |
</sect1> |
|---|
| 282 |
|
|---|
| 283 |
<sect1 id="rule-system"> |
|---|
| 284 |
<title>Système de règles de <productname>PostgreSQL</productname></title> |
|---|
| 285 |
|
|---|
| 286 |
<para> |
|---|
| 287 |
<productname>PostgreSQL</productname> supporte un puissant |
|---|
| 288 |
<firstterm>système de règles</firstterm> pour la spécification des |
|---|
| 289 |
<firstterm>vues</firstterm> et des <firstterm>mises à jour de |
|---|
| 290 |
vues</firstterm> ambigües. À l'origine, le système de règles de |
|---|
| 291 |
<productname>PostgreSQL</productname> consistait en deux implémentations : |
|---|
| 292 |
|
|---|
| 293 |
<itemizedlist> |
|---|
| 294 |
<listitem> |
|---|
| 295 |
<para> |
|---|
| 296 |
Le premier a fonctionné en utilisant le <firstterm>niveau des |
|---|
| 297 |
lignes</firstterm> et était implémenté profondément dans |
|---|
| 298 |
l'<firstterm>exécuteur</firstterm>. Le système de règles était appelé |
|---|
| 299 |
à chaque fois qu'il fallait accéder à une ligne individuelle. Cette |
|---|
| 300 |
implémentation était supprimée en 1995 quand la dernière version |
|---|
| 301 |
officielle du projet <productname>Berkeley Postgres</productname> a été |
|---|
| 302 |
transformé en <productname>Postgres95</productname>. |
|---|
| 303 |
</para> |
|---|
| 304 |
</listitem> |
|---|
| 305 |
|
|---|
| 306 |
<listitem> |
|---|
| 307 |
<para> |
|---|
| 308 |
La deuxième implémentation du système de règles est une technique appelée |
|---|
| 309 |
la <firstterm>réécriture de requêtes</firstterm>. Le <firstterm>système |
|---|
| 310 |
de réécriture</firstterm> est un module qui existe entre |
|---|
| 311 |
l'<firstterm>étape d'analyse</firstterm> et le |
|---|
| 312 |
<firstterm>planificateur/optimiseur</firstterm>. Cette technique est |
|---|
| 313 |
toujours implémentée. |
|---|
| 314 |
</para> |
|---|
| 315 |
</listitem> |
|---|
| 316 |
</itemizedlist> |
|---|
| 317 |
</para> |
|---|
| 318 |
|
|---|
| 319 |
<para> |
|---|
| 320 |
Le système de réécriture de requêtes est vue plus en détails dans le |
|---|
| 321 |
<xref linkend="rules">, donc il n'est pas nécessaire d'en parler ici. |
|---|
| 322 |
Nous indiquerons seulement qu'à la fois l'entrée et la sortie du système |
|---|
| 323 |
sont des arbres de requêtes, c'est-à-dire qu'il n'y a pas de changement |
|---|
| 324 |
dans la représentation ou le niveau du détail sémantique des arbres. La |
|---|
| 325 |
réécriture peut être imaginée comme une forme d'expansion de macro. |
|---|
| 326 |
</para> |
|---|
| 327 |
|
|---|
| 328 |
</sect1> |
|---|
| 329 |
|
|---|
| 330 |
<sect1 id="planner-optimizer"> |
|---|
| 331 |
<title>Planificateur/Optimiseur</title> |
|---|
| 332 |
|
|---|
| 333 |
<para> |
|---|
| 334 |
La tâche du <firstterm>planificateur/optimiseur</firstterm> est de créer un |
|---|
| 335 |
plan d'exécution optimal. Une requête SQL donnée (et donc, l'arbre d'une |
|---|
| 336 |
requête) peut vraiment être exécutée de plusieurs façons, chacune arrivant |
|---|
| 337 |
au même résultat. Si ce calcul est possible, l'optimiseur de la requête |
|---|
| 338 |
examinera chacun des plans d'exécution possibles pour finir par |
|---|
| 339 |
sélectionner le plan d'exécution le plus rapide. |
|---|
| 340 |
</para> |
|---|
| 341 |
|
|---|
| 342 |
<note> |
|---|
| 343 |
<para> |
|---|
| 344 |
Dans certaines situations, examiner chaque moyen d'exécuter une requête |
|---|
| 345 |
prendrait beaucoup de temps et de mémoire. En particulier, cela arrive lors |
|---|
| 346 |
de l'exécution de requêtes impliquant un grand nombre d'opérations de |
|---|
| 347 |
jointure. Pour déterminer un plan de requête raisonable (et non optimal) |
|---|
| 348 |
dans un temps raisonnable, <productname>PostgreSQL</productname> utilise |
|---|
| 349 |
un <xref linkend="geqo" endterm="geqo-title">. |
|---|
| 350 |
</para> |
|---|
| 351 |
</note> |
|---|
| 352 |
|
|---|
| 353 |
<para> |
|---|
| 354 |
Lorsque le chemin le moins coûteux est déterminé, un <firstterm>arbre |
|---|
| 355 |
plan</> est construit pour être donné à l'exécuteur. Ceci représente le |
|---|
| 356 |
plan d'exécution désiré avec suffisamment de détails pour que l'exécuteur |
|---|
| 357 |
puisse le lancer. |
|---|
| 358 |
</para> |
|---|
| 359 |
|
|---|
| 360 |
<sect2> |
|---|
| 361 |
<title>Générer les plans possibles</title> |
|---|
| 362 |
|
|---|
| 363 |
<para> |
|---|
| 364 |
Le planificateur/optimiseur décide quels plans devraient être générés |
|---|
| 365 |
suivant le type d'index défini sur les relations apparaissant dans une |
|---|
| 366 |
requête. Il y a toujours la possibilité d'exécuter un parcours séquentiel |
|---|
| 367 |
d'une relation, donc un plan utilisant seulement des parcours séquentiels |
|---|
| 368 |
est toujours créé. Supposons qu'un index soit défini sur une relation |
|---|
| 369 |
(par exemple un index B-tree) et qu'une requête contienne le filtre |
|---|
| 370 |
<literal>relation.attribut OPR constante</literal>. Si |
|---|
| 371 |
<literal>relation.attribut</literal> se trouve correspondre à la clé de |
|---|
| 372 |
l'index B-tree et que <literal>OPR</literal> soit un des opérateurs listés |
|---|
| 373 |
dans la <firstterm>classe d'opérateurs</> de l'index, un autre plan est |
|---|
| 374 |
créé en utilisant l'index B-tree pour parcourir la relation. S'il existe |
|---|
| 375 |
plusieurs index et que les restrictions de la requête font correspondre |
|---|
| 376 |
une clé d'un index, d'autres plans devront être considérés. |
|---|
| 377 |
</para> |
|---|
| 378 |
|
|---|
| 379 |
<para> |
|---|
| 380 |
Après que tous les plans probables aient été trouvés pour parcourir les |
|---|
| 381 |
relations simples, des plans pour les relations jointes sont créés. Le |
|---|
| 382 |
planificateur/optimiseur préfère considérer les jointures entre deux |
|---|
| 383 |
relations pour lesquelles il existe une clause de jointure correspondante |
|---|
| 384 |
dans la qualification <literal>WHERE</literal> (c'est-à-dire pour lequel |
|---|
| 385 |
une restriction comme <literal>where rel1.attr1=rel2.attr2</literal> |
|---|
| 386 |
existe). Des paires de jointures sans clause de jointure sont considérées |
|---|
| 387 |
seulement quand il n'existe pas d'autre choix, c'est-à-dire lorsqu'une |
|---|
| 388 |
relation particulière n'a pas de clauses jointes disponibles sans autre |
|---|
| 389 |
relation. Tous les plans possibles sont générés pour chaque paire de |
|---|
| 390 |
jointure considérée par le planificateur/optimiseur. Les trois stratégies |
|---|
| 391 |
possibles de jointure sont : |
|---|
| 392 |
|
|---|
| 393 |
<itemizedlist> |
|---|
| 394 |
<listitem> |
|---|
| 395 |
<para> |
|---|
| 396 |
<firstterm>jointure en boucle imbriquée</firstterm> (NdT : nested loop |
|---|
| 397 |
join) : la relation droite est parcourue une fois pour chaque ligne |
|---|
| 398 |
trouvée dans la relation gauche. Cette stratégie est facile à |
|---|
| 399 |
implémenter mais peut être très consommatrice en temps. (Néanmoins, si |
|---|
| 400 |
la relation droite peut être parcourue suivant un parcours indexé, ceci |
|---|
| 401 |
peut être une bonne stratégie. Il est possible d'utiliser des valeurs |
|---|
| 402 |
provenant de la ligne actuelle de la relation gauche comme clés pour |
|---|
| 403 |
un parcours indexé à droite.) |
|---|
| 404 |
</para> |
|---|
| 405 |
</listitem> |
|---|
| 406 |
|
|---|
| 407 |
<listitem> |
|---|
| 408 |
<para> |
|---|
| 409 |
<firstterm>jointure tri et assemblement</firstterm> (NdT : merge sort |
|---|
| 410 |
join) : Chaque relation est triée sur les attributs de la jointure avant |
|---|
| 411 |
que la jointure ne commence. Puis, les deux relations sont assemblées en |
|---|
| 412 |
prenant en compte que les deux relations sont triées sur les attributs |
|---|
| 413 |
de jointure. Ce type de jointure est plus attractif parce que chaque |
|---|
| 414 |
relation doit être parcourue seulement une fois. |
|---|
| 415 |
</para> |
|---|
| 416 |
</listitem> |
|---|
| 417 |
|
|---|
| 418 |
<listitem> |
|---|
| 419 |
<para> |
|---|
| 420 |
<firstterm>jointure de découpage</firstterm> (hash join) : la |
|---|
| 421 |
relation droite est tout d'abord parcourue et chargée dans une table de |
|---|
| 422 |
découpage en utilisant ses attributs de jointure comme clés de |
|---|
| 423 |
découpage. Ensuite, la relation gauche est parcourue et les valeurs |
|---|
| 424 |
appropriées de chaque ligne trouvée sont utilisées comme clés de |
|---|
| 425 |
découpage pour localiser les lignes correspondantes dans la table. |
|---|
| 426 |
</para> |
|---|
| 427 |
</listitem> |
|---|
| 428 |
</itemizedlist> |
|---|
| 429 |
</para> |
|---|
| 430 |
|
|---|
| 431 |
<para> |
|---|
| 432 |
L'arbre de plan terminé consiste en des parcours séquentiels ou indexés des |
|---|
| 433 |
relations de base, plus les nœuds des jointures en boucle, jointures |
|---|
| 434 |
tri et rassemblement, et jointures de découpage si nécessaire, plus toutes |
|---|
| 435 |
les étapes auxiliaires nécessaires, tels que les nœuds de tri ou les |
|---|
| 436 |
nœuds de calcul des fonctions d'agrégat. La plupart des types de |
|---|
| 437 |
nœud de plans ont la capacité supplémentaire de faire une |
|---|
| 438 |
<firstterm>sélection</> (annulant les lignes qui ne correspondent pas à une |
|---|
| 439 |
condition booléenne spécifiée) et une <firstterm>projection</> (calcul d'un |
|---|
| 440 |
ensemble de colonnes dérivées basé sur des valeurs de colonnes données, |
|---|
| 441 |
c'est-à-dire l'évaluation des expressions scalaires si nécessaire). Une des |
|---|
| 442 |
responsabilités du planificateur est d'attacher les conditions de |
|---|
| 443 |
sélection à partir de la clause <literal>WHERE</literal> et du calcul des |
|---|
| 444 |
expressions de calculs requises aux nœuds les plus appropriés de |
|---|
| 445 |
l'arbre de plan. |
|---|
| 446 |
</para> |
|---|
| 447 |
</sect2> |
|---|
| 448 |
</sect1> |
|---|
| 449 |
|
|---|
| 450 |
<sect1 id="executor"> |
|---|
| 451 |
<title>Exécuteur</title> |
|---|
| 452 |
|
|---|
| 453 |
<para> |
|---|
| 454 |
L'<firstterm>exécuteur</firstterm> prend le plan envoyé par le |
|---|
| 455 |
planificateur/optimiseur et l'exécute récursivement pour extraire |
|---|
| 456 |
l'ensemble requis de lignes. Ceci est essentiellement un mécanisme de |
|---|
| 457 |
pipeline en demande-envoi. Chaque fois qu'un nœud du plan est appelé, |
|---|
| 458 |
il doit apporter une ligne supplémentaire ou indiquer qu'il a fini d'envoyer |
|---|
| 459 |
des lignes. |
|---|
| 460 |
</para> |
|---|
| 461 |
|
|---|
| 462 |
<para> |
|---|
| 463 |
Pour donner un exemple concret, supposez que le nœud supérieur soit un |
|---|
| 464 |
nœud <literal>MergeJoin</literal>. Avant qu'une fusion puisse être |
|---|
| 465 |
faite, deux lignes doivent être récupérées (une pour chaque sous-plan). |
|---|
| 466 |
Donc, l'exécuteur s'appelle récursivement pour exécuter les sous-plans (il |
|---|
| 467 |
commence avec le sous-plan attaché à l'<literal>arbre gauche</literal>). |
|---|
| 468 |
Le nouveau nœud supérieur (le nœud supérieur du sous-plan |
|---|
| 469 |
gauche) est, disons, un nœud <literal>Sort</literal> (NdT : Tri) |
|---|
| 470 |
et une récursion est une nouvelle fois nécessaire pour obtenir une ligne en |
|---|
| 471 |
entrée. Le nœud fils de <literal>Sort</literal> pourrait être un |
|---|
| 472 |
nœud <literal>SeqScan</>, représentant la lecture réelle d'une table. |
|---|
| 473 |
L'exécution de ce nœud fait que l'exécuteur récupère une ligne à |
|---|
| 474 |
partir de la table et la renvoie au nœud appelant. Le nœud |
|---|
| 475 |
<literal>Sort</literal> appellera de façon répétée son fils pour obtenir |
|---|
| 476 |
toutes les lignes à trier. Quand l'entrée est terminée (indiqué par le |
|---|
| 477 |
nœud fils renvoyant un NULL au lieu d'une ligne), le code de |
|---|
| 478 |
<literal>Sort</literal> est enfin capable de renvoyer sa première ligne en |
|---|
| 479 |
sortie, c'est-à-dire le premier suivant l'ordre de tri. Il conserve les |
|---|
| 480 |
lignes restantes de façon à les renvoyer dans le bon ordre en réponse à des |
|---|
| 481 |
demandes ultérieures. |
|---|
| 482 |
</para> |
|---|
| 483 |
|
|---|
| 484 |
<para> |
|---|
| 485 |
Le nœud <literal>MergeJoin</literal> demande de la même façon la |
|---|
| 486 |
première ligne à partir du sous-plan droit. Ensuite, il compare les deux |
|---|
| 487 |
lignes pour savoir si elles peuvent être jointes ; si c'est le cas, il |
|---|
| 488 |
renvoie la ligne de jointure à son appelant. Au prochain appel, ou |
|---|
| 489 |
immédiatement s'il ne peut pas joindre la paire actuelle d'entrées, il |
|---|
| 490 |
avance sur la prochaine ligne d'une des deux tables (suivant le résultat de |
|---|
| 491 |
la comparaison), et vérifie de nouveau la correspondance. Éventuellement, un |
|---|
| 492 |
sous-plan est terminé et le nœud <literal>MergeJoin</literal> renvoie |
|---|
| 493 |
NULL pour indiquer qu'il n'y a plus de lignes jointes à former. |
|---|
| 494 |
</para> |
|---|
| 495 |
|
|---|
| 496 |
<para> |
|---|
| 497 |
Les requêtes complexes peuvent nécessiter un grand nombre de niveaux de |
|---|
| 498 |
nœuds pour les plans, mais l'approche générale est la même : |
|---|
| 499 |
chaque nœud est exécuté et renvoie sa prochaine ligne en sortie à |
|---|
| 500 |
chaque fois qu'il est appelé. Chaque nœud est responsable aussi de |
|---|
| 501 |
l'application de toute expression de sélection ou projection qui lui ont été |
|---|
| 502 |
confiées par le planificateur. |
|---|
| 503 |
</para> |
|---|
| 504 |
|
|---|
| 505 |
<para> |
|---|
| 506 |
Le mécanisme de l'exécuteur est utilisé pour évaluer les quatre types de |
|---|
| 507 |
requêtes de base en SQL : <command>SELECT</>, <command>INSERT</>, |
|---|
| 508 |
<command>UPDATE</> et <command>DELETE</>. Pour <command>SELECT</>, le code |
|---|
| 509 |
de l'exécuteur au niveau supérieur a seulement besoin d'envoyer chaque ligne |
|---|
| 510 |
retournée par l'arbre plan de la requête vers le client. Pour |
|---|
| 511 |
<command>INSERT</>, chaque ligne renvoyée est insérée dans la table cible |
|---|
| 512 |
spécifiée par le <command>INSERT</>. (Une simple commande |
|---|
| 513 |
<command>INSERT ... VALUES</> crée un arbre plan trivial consistant en un |
|---|
| 514 |
seul nœud, <literal>Result</>, calculant une seule ligne de résultat. |
|---|
| 515 |
Mais <command>INSERT ... SELECT</> pourrait demander la pleine puissance du |
|---|
| 516 |
mécanisme de l'exécuteur.) Pour <command>UPDATE</>, le planificateur |
|---|
| 517 |
s'arrange pour que chaque ligne calculée inclue toutes les valeurs mises à |
|---|
| 518 |
jour des colonnes, plus le <firstterm>TID</> (tuple ID ou l'identifiant de |
|---|
| 519 |
la ligne) de la ligne de la cible originale ; l'exécuteur de haut |
|---|
| 520 |
niveau utilise cette information pour créer une nouvelle ligne mise à jour |
|---|
| 521 |
et pour marquer la suppression de l'ancienne ligne. Pour |
|---|
| 522 |
<command>DELETE</>, la seule colonne renvoyée par le plan est le TID, et |
|---|
| 523 |
l'exécuteur de haut niveau utilise simplement le TID pour visiter les |
|---|
| 524 |
lignes cible et les marquer comme supprimées. |
|---|
| 525 |
</para> |
|---|
| 526 |
|
|---|
| 527 |
</sect1> |
|---|
| 528 |
|
|---|
| 529 |
</chapter> |
|---|
| 530 |
|
|---|
| 531 |
<!-- Keep this comment at the end of the file |
|---|
| 532 |
Local variables: |
|---|
| 533 |
mode:sgml |
|---|
| 534 |
sgml-omittag:nil |
|---|
| 535 |
sgml-shorttag:t |
|---|
| 536 |
sgml-minimize-attributes:nil |
|---|
| 537 |
sgml-always-quote-attributes:t |
|---|
| 538 |
sgml-indent-step:1 |
|---|
| 539 |
sgml-indent-data:t |
|---|
| 540 |
sgml-parent-document:nil |
|---|
| 541 |
sgml-default-dtd-file:"./reference.ced" |
|---|
| 542 |
sgml-exposed-tags:nil |
|---|
| 543 |
sgml-local-catalogs:("/usr/lib/sgml/catalog") |
|---|
| 544 |
sgml-local-ecat-files:nil |
|---|
| 545 |
End: |
|---|
| 546 |
--> |
|---|