| 1 |
<!-- $Header: /var/lib/cvs/pgsql-fr/sgml/indices.sgml,v 1.8.2.2 2005/07/15 06:33:40 guillaume Exp $ --> |
|---|
| 2 |
|
|---|
| 3 |
<chapter id="indexes"> |
|---|
| 4 |
<title id="indexes-title">Index</title> |
|---|
| 5 |
|
|---|
| 6 |
<indexterm zone="indexes"> |
|---|
| 7 |
<primary>index</primary> |
|---|
| 8 |
</indexterm> |
|---|
| 9 |
|
|---|
| 10 |
<para> |
|---|
| 11 |
Les index sont une méthode courante pour augmenter les performances |
|---|
| 12 |
d'une base de données. Un index permet au serveur de bases de données de |
|---|
| 13 |
retrouver une ligne spécifique bien plus rapidement que sans index. |
|---|
| 14 |
Mais les index ajoutent aussi une surcharge au système de base de données |
|---|
| 15 |
dans son ensemble, si bien qu'ils doivent être utilisés avec discernement. |
|---|
| 16 |
</para> |
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
<sect1 id="index-intro"> |
|---|
| 20 |
<title>Introduction</title> |
|---|
| 21 |
|
|---|
| 22 |
<para> |
|---|
| 23 |
Supposons que nous ayons une table comme celle-ci: |
|---|
| 24 |
<programlisting> |
|---|
| 25 |
CREATE TABLE test1 ( |
|---|
| 26 |
id integer, |
|---|
| 27 |
content varchar |
|---|
| 28 |
); |
|---|
| 29 |
</programlisting> |
|---|
| 30 |
et que l'application utilise beaucoup de requêtes de la forme |
|---|
| 31 |
<programlisting> |
|---|
| 32 |
SELECT content FROM test1 WHERE id = <replaceable>constant</replaceable>; |
|---|
| 33 |
</programlisting> |
|---|
| 34 |
Sans préparation supplémentaire, le système devrait lire la table |
|---|
| 35 |
<structname>test1</structname> en entier, ligne par ligne, pour trouver |
|---|
| 36 |
toutes les lignes qui correspondent. S'il y a beaucoup de lignes dans |
|---|
| 37 |
<structname>test1</structname>, et que seulement quelques lignes |
|---|
| 38 |
correspondent à la requête (peut-être même zéro ou une seule), alors, |
|---|
| 39 |
clairement, la méthode n'est pas efficace. |
|---|
| 40 |
Mais si on a indiqué au système de maintenir un index sur la colonne |
|---|
| 41 |
<structfield>id</structfield>, alors il peut utiliser une manière |
|---|
| 42 |
beaucoup plus efficace pour trouver les lignes recherchées. Par exemple, |
|---|
| 43 |
il pourrait n'avoir à parcourir que quelques niveaux d'un arbre de |
|---|
| 44 |
recherche. |
|---|
| 45 |
</para> |
|---|
| 46 |
|
|---|
| 47 |
<para> |
|---|
| 48 |
Une approche similaire est utilisée dans la plupart des livres autres |
|---|
| 49 |
que ceux de fiction: les termes et concepts qui sont fréquemment |
|---|
| 50 |
recherchés par les lecteurs sont listés par ordre alphabétique à la |
|---|
| 51 |
fin du livre. Le lecteur qui recherche un mot particulier peut |
|---|
| 52 |
facilement parcourir l'index, puis aller directement à la page |
|---|
| 53 |
ou aux pages indiquée(s). De la même façon que l'auteur doit anticiper |
|---|
| 54 |
les sujets que les lecteurs risquent de rechercher, il est de la |
|---|
| 55 |
responsabilité du programmeur de prévoir quels index seraient avantageux. |
|---|
| 56 |
</para> |
|---|
| 57 |
|
|---|
| 58 |
<para> |
|---|
| 59 |
La commande suivante permet de créer un index sur la colonne |
|---|
| 60 |
<structfield>id</structfield> dont nous parlons: |
|---|
| 61 |
<programlisting> |
|---|
| 62 |
CREATE INDEX test1_id_index ON test1 (id); |
|---|
| 63 |
</programlisting> |
|---|
| 64 |
Le nom <structname>test1_id_index</structname> peut être choisi |
|---|
| 65 |
librement, mais il est conseillé de choisir un nom qui rappelle le but |
|---|
| 66 |
de l'index. |
|---|
| 67 |
</para> |
|---|
| 68 |
|
|---|
| 69 |
<para> |
|---|
| 70 |
Pour supprimer l'index, utilisez la commande <command>DROP INDEX</command>. |
|---|
| 71 |
Les index peuvent être ajoutés et enlevés des tables à tout moment. |
|---|
| 72 |
</para> |
|---|
| 73 |
|
|---|
| 74 |
<para> |
|---|
| 75 |
Une fois l'index créé, aucune intervention supplémentaire n'est nécessaire: |
|---|
| 76 |
Le système met à jour l'index lorsque la table est modifiée, et utilise |
|---|
| 77 |
l'index dans les requêtes lorsqu'il pense que c'est plus efficace qu'une |
|---|
| 78 |
lecture complète de la table. Il faut néanmoins lancer la commande |
|---|
| 79 |
<command>ANALYZE</command> régulièrement pour permettre à l'optimiseur |
|---|
| 80 |
de requêtes de prendre les bonnes décisions. |
|---|
| 81 |
Voyez <xref linkend="performance-tips"> pour comprendre quand et |
|---|
| 82 |
pourquoi l'optimiseur décide d'utiliser ou de ne |
|---|
| 83 |
<emphasis>pas</emphasis> utiliser un index. |
|---|
| 84 |
</para> |
|---|
| 85 |
|
|---|
| 86 |
<para> |
|---|
| 87 |
Les index peuvent aussi bénéficier aux commandes |
|---|
| 88 |
<command>UPDATE</command> et <command>DELETE</command> qui ont des |
|---|
| 89 |
conditions de recherche. Les index peuvent de plus être utilisés dans les |
|---|
| 90 |
jointures. Ainsi, un index défini sur une colonne qui fait partie d'une |
|---|
| 91 |
condition de jointure peut accélérer significativement les requêtes avec |
|---|
| 92 |
jointures. |
|---|
| 93 |
</para> |
|---|
| 94 |
|
|---|
| 95 |
<para> |
|---|
| 96 |
Lorsqu'un index est créé, le système doit le maintenir synchronisé avec |
|---|
| 97 |
la table. Cela rend plus lourdes les opérations de manipulation de |
|---|
| 98 |
données. C'est pourquoi les index qui ne sont pas essentiels ou qui ne |
|---|
| 99 |
sont pas utilisés du tout doivent être supprimés. Notez qu'une requête ou |
|---|
| 100 |
une commande de manipulation de données ne peut utiliser qu'un index par |
|---|
| 101 |
table au maximum. |
|---|
| 102 |
</para> |
|---|
| 103 |
</sect1> |
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
<sect1 id="indexes-types"> |
|---|
| 107 |
<title>Types d'index</title> |
|---|
| 108 |
|
|---|
| 109 |
<para> |
|---|
| 110 |
<productname>PostgreSQL</productname> propose plusieurs types d'index: |
|---|
| 111 |
B-tree, R-tree, GiST et Hash. Chaque type d'index utilise un algorithme |
|---|
| 112 |
différent qui convient à un type particulier de requêtes. |
|---|
| 113 |
<indexterm> |
|---|
| 114 |
<primary>index</primary> |
|---|
| 115 |
<secondary>B-tree</secondary> |
|---|
| 116 |
</indexterm> |
|---|
| 117 |
<indexterm> |
|---|
| 118 |
<primary>B-tree</primary> |
|---|
| 119 |
<see>index</see> |
|---|
| 120 |
</indexterm> |
|---|
| 121 |
Par défaut, la commande <command>CREATE INDEX</command> crée un index |
|---|
| 122 |
de type B-tree, qui convient à la plupart des situations. Les index |
|---|
| 123 |
B-tree savent traiter les égalités et les recherches sur des tranches de |
|---|
| 124 |
valeurs sur les données qui peuvent être triées. En particulier, |
|---|
| 125 |
l'optimiseur de requêtes de <productname>PostgreSQL</productname> |
|---|
| 126 |
essaie d'utiliser un index B-tree lorsque une colonne indexée est |
|---|
| 127 |
utilisée dans une comparaison qui utilise un de ces opérateurs: |
|---|
| 128 |
|
|---|
| 129 |
<simplelist type="inline"> |
|---|
| 130 |
<member><literal><</literal></member> |
|---|
| 131 |
<member><literal><=</literal></member> |
|---|
| 132 |
<member><literal>=</literal></member> |
|---|
| 133 |
<member><literal>>=</literal></member> |
|---|
| 134 |
<member><literal>></literal></member> |
|---|
| 135 |
</simplelist> |
|---|
| 136 |
</para> |
|---|
| 137 |
|
|---|
| 138 |
<para> |
|---|
| 139 |
L'optimiseur peut aussi utiliser un index B-tree pour des requêtes |
|---|
| 140 |
qui utilisent les opérateurs de recherche de motif <literal>LIKE</literal>, |
|---|
| 141 |
<literal>ILIKE</literal>, <literal>~</literal>, et |
|---|
| 142 |
<literal>~*</literal>, <emphasis>si</emphasis> le motif est au début |
|---|
| 143 |
de la chaîne à rechercher. Par exemple: <literal>col LIKE |
|---|
| 144 |
'foo%'</literal> ou <literal>col ~ '^foo'</literal>, mais pas |
|---|
| 145 |
<literal>col LIKE '%bar'</literal>. Néanmoins, si votre serveur |
|---|
| 146 |
n'utilise pas la localisation C, il vous faudra créer l'index avec |
|---|
| 147 |
une classe d'opérateur spéciale. Voir <xref linkend="indexes-opclass"> |
|---|
| 148 |
plus bas. |
|---|
| 149 |
</para> |
|---|
| 150 |
|
|---|
| 151 |
<para> |
|---|
| 152 |
<indexterm> |
|---|
| 153 |
<primary>index</primary> |
|---|
| 154 |
<secondary>R-tree</secondary> |
|---|
| 155 |
</indexterm> |
|---|
| 156 |
<indexterm> |
|---|
| 157 |
<primary>R-tree</primary> |
|---|
| 158 |
<see>index</see> |
|---|
| 159 |
</indexterm> |
|---|
| 160 |
Les index R-tree sont adaptés aux requêtes sur des données spatiales. |
|---|
| 161 |
Pour créer un index R-tree, utilisez une commande de la forme: |
|---|
| 162 |
<synopsis> |
|---|
| 163 |
CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING RTREE (<replaceable>column</replaceable>); |
|---|
| 164 |
</synopsis> |
|---|
| 165 |
L'optimiseur de requêtes de <productname>PostgreSQL</productname> |
|---|
| 166 |
envisagera d'utiliser un index R-tree lorsqu'une colonne indexée fait |
|---|
| 167 |
partie d'une comparaison utilisant l'un de ces opérateurs: |
|---|
| 168 |
|
|---|
| 169 |
<simplelist type="inline"> |
|---|
| 170 |
<member><literal><<</literal></member> |
|---|
| 171 |
<member><literal>&<</literal></member> |
|---|
| 172 |
<member><literal>&></literal></member> |
|---|
| 173 |
<member><literal>>></literal></member> |
|---|
| 174 |
<member><literal>@</literal></member> |
|---|
| 175 |
<member><literal>~=</literal></member> |
|---|
| 176 |
<member><literal>&&</literal></member> |
|---|
| 177 |
</simplelist> |
|---|
| 178 |
(Référez vous à <xref linkend="functions-geometry"> pour connaître la |
|---|
| 179 |
signification de ces opérateurs.) |
|---|
| 180 |
</para> |
|---|
| 181 |
|
|---|
| 182 |
<para> |
|---|
| 183 |
<indexterm> |
|---|
| 184 |
<primary>index</primary> |
|---|
| 185 |
<secondary>hash</secondary> |
|---|
| 186 |
</indexterm> |
|---|
| 187 |
<indexterm> |
|---|
| 188 |
<primary>hash</primary> |
|---|
| 189 |
<see>index</see> |
|---|
| 190 |
</indexterm> |
|---|
| 191 |
Les index hachés ne supportent que les simples comparaisons d'égalité. |
|---|
| 192 |
L'optimiseur de requêtes envisagera d'utiliser un index haché lorsqu'une |
|---|
| 193 |
colonne indexée fait partie d'une comparaison utilisant l'opérateur |
|---|
| 194 |
<literal>=</literal>. La commande suivante est utilisée pour créer un index |
|---|
| 195 |
haché: |
|---|
| 196 |
<synopsis> |
|---|
| 197 |
CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING HASH (<replaceable>column</replaceable>); |
|---|
| 198 |
</synopsis> |
|---|
| 199 |
<note> |
|---|
| 200 |
<para> |
|---|
| 201 |
Les tests ont montré que les index hachés de |
|---|
| 202 |
<productname>PostgreSQL</productname> ne sont pas plus efficaces que les |
|---|
| 203 |
index B-tree, et que la taille de l'index et le temps de création d'un |
|---|
| 204 |
index hashé sont bien moins bons. Pour ces raisons, l'utilisation des |
|---|
| 205 |
index hachés est actuellement découragée. |
|---|
| 206 |
</para> |
|---|
| 207 |
</note> |
|---|
| 208 |
</para> |
|---|
| 209 |
|
|---|
| 210 |
<para> |
|---|
| 211 |
La méthode d'index B-tree est une implémentation des B-trees à haute |
|---|
| 212 |
concurrence de Lehman-Yao. La méthode d'index R-tree implémente |
|---|
| 213 |
les R-tree standards en utilisant l'algorithme de découpage quadratique |
|---|
| 214 |
de Guttman. La méthode d'index par hachage est une implémentation de |
|---|
| 215 |
l'algorithme de hachage linéaire de Litwin. |
|---|
| 216 |
Nous ne mentionnons les algorithmes utilisés que pour indiquer que toutes |
|---|
| 217 |
ces méthodes d'indexation sont complètement dynamiques et n'ont pas |
|---|
| 218 |
besoin d'une optimisation périodique (au contraire, par exemple, des |
|---|
| 219 |
méthodes de hachage statique). |
|---|
| 220 |
</para> |
|---|
| 221 |
</sect1> |
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 |
<sect1 id="indexes-multicolumn"> |
|---|
| 225 |
<title>Les index multicolonnes</title> |
|---|
| 226 |
|
|---|
| 227 |
<indexterm zone="indexes-multicolumn"> |
|---|
| 228 |
<primary>index</primary> |
|---|
| 229 |
<secondary>multi colonne</secondary> |
|---|
| 230 |
</indexterm> |
|---|
| 231 |
|
|---|
| 232 |
<para> |
|---|
| 233 |
Un index peut porter sur plus d'une colonne. Par exemple, si vous avez |
|---|
| 234 |
une table de cette forme: |
|---|
| 235 |
<programlisting> |
|---|
| 236 |
CREATE TABLE test2 ( |
|---|
| 237 |
majeur int, |
|---|
| 238 |
mineur int, |
|---|
| 239 |
nom varchar |
|---|
| 240 |
); |
|---|
| 241 |
</programlisting> |
|---|
| 242 |
(par exemple, si vous gardez votre répertoire |
|---|
| 243 |
<filename class="directory">/dev</filename> dans une base de données...) |
|---|
| 244 |
et que vous faites fréquemment des requêtes comme: |
|---|
| 245 |
<programlisting> |
|---|
| 246 |
SELECT nom FROM test2 WHERE majeur = <replaceable>constante</replaceable> AND mineur = <replaceable>constante</replaceable>; |
|---|
| 247 |
</programlisting> |
|---|
| 248 |
alors il est sans doute souhaitable de définir un index sur les colonnes |
|---|
| 249 |
<structfield>majeur</structfield> et |
|---|
| 250 |
<structfield>mineur</structfield> ensemble, par exemple avec:, |
|---|
| 251 |
<programlisting> |
|---|
| 252 |
CREATE INDEX test2_mm_idx ON test2 (majeur, mineur); |
|---|
| 253 |
</programlisting> |
|---|
| 254 |
</para> |
|---|
| 255 |
|
|---|
| 256 |
<para> |
|---|
| 257 |
Actuellement, seuls les B-trees et les index GiST supportent les |
|---|
| 258 |
index multicolonnes. Jusqu'à 32 colonnes peuvent être indexées. |
|---|
| 259 |
Cette limite peut être modifiée à la compilation de |
|---|
| 260 |
<productname>PostgreSQL</productname>. Voyez le fichier |
|---|
| 261 |
<filename>pg_config_manual.h</filename>. |
|---|
| 262 |
</para> |
|---|
| 263 |
|
|---|
| 264 |
<para> |
|---|
| 265 |
L'optimiseur de requêtes peut utiliser un index multicolonnes pour les |
|---|
| 266 |
requêtes qui utilisent la colonnes la plus à gauche de la définition de |
|---|
| 267 |
l'index, plus un nombre quelconque des colonnes listées à sa droite, sans |
|---|
| 268 |
trou. Par exemple, un index sur <literal>(a, b, c)</literal> peut |
|---|
| 269 |
être utilisé dans des requêtes utilisant <literal>a</literal>, |
|---|
| 270 |
<literal>b</literal>, et <literal>c</literal>, ou dans des requêtes |
|---|
| 271 |
utilisant à la fois <literal>a</literal> et <literal>b</literal>, |
|---|
| 272 |
ou dans des requêtes n'utilisant que <literal>a</literal>, mais pas dans |
|---|
| 273 |
une requête utilisant une autre combinaison. (Dans une requête utilisant |
|---|
| 274 |
<literal>a</literal> et <literal>c</literal>, l'optimiseur pourrait choisir |
|---|
| 275 |
d'utiliser l'index pour <literal>a</literal>, en traitant |
|---|
| 276 |
<literal>c</literal> comme une colonne ordinaire non indexée.) |
|---|
| 277 |
Bien sur, chaque colonne doit être utilisée avec les opérateurs appropriés |
|---|
| 278 |
pour le type d'index. Les clauses qui comprennent un autre opérateur ne |
|---|
| 279 |
seront pas prises en compte. |
|---|
| 280 |
</para> |
|---|
| 281 |
|
|---|
| 282 |
<para> |
|---|
| 283 |
Les index multicolonnes ne peuvent être utilisés que si les |
|---|
| 284 |
clauses des colonnes indexées sont jointes avec <literal>AND</literal>. |
|---|
| 285 |
Par exemple, |
|---|
| 286 |
<programlisting> |
|---|
| 287 |
SELECT nom FROM test2 WHERE majeur = <replaceable>constant</replaceable> OR mineur = <replaceable>constant</replaceable>; |
|---|
| 288 |
</programlisting> |
|---|
| 289 |
ne peut utiliser l'index <structname>test2_mm_idx</structname> (défini |
|---|
| 290 |
précédemment) sur les deux colonnes. (Il peut néanmoins l'utiliser pour |
|---|
| 291 |
faire une recherche sur la colonne <structfield>major</structfield>.) |
|---|
| 292 |
</para> |
|---|
| 293 |
|
|---|
| 294 |
<para> |
|---|
| 295 |
Les index multicolonnes doivent être utilisés avec parcimonie. La plupart |
|---|
| 296 |
du temps, un index sur une seule colonne est suffisant et économise du |
|---|
| 297 |
temps et de l'espace disque. Les index avec plus de trois colonnes sont |
|---|
| 298 |
rarement utiles, sauf cas très particuliers. |
|---|
| 299 |
</para> |
|---|
| 300 |
</sect1> |
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 |
<sect1 id="indexes-unique"> |
|---|
| 304 |
<title>Index Uniques</title> |
|---|
| 305 |
|
|---|
| 306 |
<indexterm zone="indexes-unique"> |
|---|
| 307 |
<primary>index</primary> |
|---|
| 308 |
<secondary>unique</secondary> |
|---|
| 309 |
</indexterm> |
|---|
| 310 |
|
|---|
| 311 |
<para> |
|---|
| 312 |
Les index peuvent aussi être utilisés pour garantir l'unicité des |
|---|
| 313 |
valeurs d'une colonne, ou l'unicité des valeurs combinées de plusieurs |
|---|
| 314 |
colonnes. |
|---|
| 315 |
<synopsis> |
|---|
| 316 |
CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <optional>, ...</optional>); |
|---|
| 317 |
</synopsis> |
|---|
| 318 |
À ce jour, seuls les index B-trees peuvent être déclarés uniques. |
|---|
| 319 |
</para> |
|---|
| 320 |
|
|---|
| 321 |
<para> |
|---|
| 322 |
Lorsqu'un index est déclaré unique, des lignes différentes d'une table ne |
|---|
| 323 |
pourront avoir une valeur égale. Les valeurs Nulles ne sont pas |
|---|
| 324 |
considérées comme égales. Un index unique multicolonnes ne rejettera que les |
|---|
| 325 |
cas où toutes les colonnes indexées sont égales dans deux lignes. |
|---|
| 326 |
</para> |
|---|
| 327 |
|
|---|
| 328 |
<para> |
|---|
| 329 |
<productname>PostgreSQL</productname> crée automatiquement un index |
|---|
| 330 |
unique quand une contrainte unique ou une clé primaire sont définies |
|---|
| 331 |
sur une table. L'index porte sur les colonnes qui composent la clé primaire |
|---|
| 332 |
ou la contrainte d'unicité (il s'agit d'un index multicolonnes, si c'est |
|---|
| 333 |
approprié). Cet index EST le mécanisme qui vérifie la contrainte. |
|---|
| 334 |
</para> |
|---|
| 335 |
|
|---|
| 336 |
<note> |
|---|
| 337 |
<para> |
|---|
| 338 |
La méthode la plus appropriée pour ajouter une contrainte à une table |
|---|
| 339 |
est <literal>ALTER TABLE ... ADD CONSTRAINT</literal>. L'utilisation des |
|---|
| 340 |
index pour vérifier les contraintes uniques doit être considérée comme |
|---|
| 341 |
un détail d'implémentation qui ne doit pas être utilisé directement. |
|---|
| 342 |
Il faut par contre savoir qu'il n'est pas nécessaire de créer manuellement |
|---|
| 343 |
un index sur les colonnes uniques. Cela dupliquerait l'index créé |
|---|
| 344 |
automatiquement. |
|---|
| 345 |
</para> |
|---|
| 346 |
</note> |
|---|
| 347 |
</sect1> |
|---|
| 348 |
|
|---|
| 349 |
|
|---|
| 350 |
<sect1 id="indexes-expressional"> |
|---|
| 351 |
<title>Index sur des expressions</title> |
|---|
| 352 |
|
|---|
| 353 |
<indexterm zone="indexes-expressional"> |
|---|
| 354 |
<primary>index</primary> |
|---|
| 355 |
<secondary sortas="expressions">sur des expressions</secondary> |
|---|
| 356 |
</indexterm> |
|---|
| 357 |
|
|---|
| 358 |
<para> |
|---|
| 359 |
Une colonne d'index ne correspond pas nécessairement exactement à une |
|---|
| 360 |
colonne de la table associée, mais peut être une fonction ou une expression |
|---|
| 361 |
scalaire calculée à partir d'une ou plusieurs colonnes de la table. |
|---|
| 362 |
Cette fonctionnalité est utile pour obtenir un accès rapide aux tables |
|---|
| 363 |
basé sur les résultat des calculs. |
|---|
| 364 |
</para> |
|---|
| 365 |
|
|---|
| 366 |
<para> |
|---|
| 367 |
Par exemple, une façon classique de faire des comparaisons indépendantes |
|---|
| 368 |
de la casse est d'utiliser la fonction <function>lower</function>: |
|---|
| 369 |
<programlisting> |
|---|
| 370 |
SELECT * FROM test1 WHERE lower(col1) = 'value'; |
|---|
| 371 |
</programlisting> |
|---|
| 372 |
Si un index a été défini sur le résultat de <literal>lower(col1)</literal>, |
|---|
| 373 |
cette requête peut l'utiliser. |
|---|
| 374 |
Cet index est créé avec la commande: |
|---|
| 375 |
<programlisting> |
|---|
| 376 |
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1)); |
|---|
| 377 |
</programlisting> |
|---|
| 378 |
</para> |
|---|
| 379 |
|
|---|
| 380 |
<para> |
|---|
| 381 |
Si nous avions déclaré cet index <literal>UNIQUE</literal>, il empêcherait |
|---|
| 382 |
la création de lignes dont la valeur de la colonne <literal>col1</literal> ne |
|---|
| 383 |
diffère que par la casse. |
|---|
| 384 |
Ainsi, les index sur les expressions peuvent être utilisés pour |
|---|
| 385 |
vérifier des contraintes qui ne peuvent êtres définies avec une simple |
|---|
| 386 |
contrainte. |
|---|
| 387 |
</para> |
|---|
| 388 |
|
|---|
| 389 |
<para> |
|---|
| 390 |
Un autre exemple, si vous faites souvent des requêtes comme celle-ci: |
|---|
| 391 |
<programlisting> |
|---|
| 392 |
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith'; |
|---|
| 393 |
</programlisting> |
|---|
| 394 |
alors il peut être utile de créer un index comme celui-ci: |
|---|
| 395 |
<programlisting> |
|---|
| 396 |
CREATE INDEX people_names ON people ((first_name || ' ' || last_name)); |
|---|
| 397 |
</programlisting> |
|---|
| 398 |
</para> |
|---|
| 399 |
|
|---|
| 400 |
<para> |
|---|
| 401 |
La syntaxe de la commande <command>CREATE INDEX</command> nécessite normalement |
|---|
| 402 |
de mettre des parenthèses autour de l'expression indexés, comme dans |
|---|
| 403 |
l'exemple précédent. Les parenthèses peuvent être omises quand l'expression |
|---|
| 404 |
est juste un appel de fonction, comme dans le premier exemple. |
|---|
| 405 |
</para> |
|---|
| 406 |
|
|---|
| 407 |
<para> |
|---|
| 408 |
Les expressions d'index sont relativement coûteuses à calculer, car |
|---|
| 409 |
l'expression doit être recalculée à chaque insertion ou mise à jour de |
|---|
| 410 |
chaque ligne. C'est pourquoi les index basés sur des expressions ne |
|---|
| 411 |
doivent être utilisés que quand les requêtes qui les exécutent sont très |
|---|
| 412 |
fréquentes. |
|---|
| 413 |
</para> |
|---|
| 414 |
</sect1> |
|---|
| 415 |
|
|---|
| 416 |
|
|---|
| 417 |
<sect1 id="indexes-opclass"> |
|---|
| 418 |
<title>Classes d'Opérateurs</title> |
|---|
| 419 |
|
|---|
| 420 |
<indexterm zone="indexes-opclass"> |
|---|
| 421 |
<primary>classe d'opérateurs</primary> |
|---|
| 422 |
</indexterm> |
|---|
| 423 |
|
|---|
| 424 |
<para> |
|---|
| 425 |
Une définition d'index peut indiquer une <firstterm>classe |
|---|
| 426 |
d'opérateurs</firstterm> pour chaque colonne de l'index. |
|---|
| 427 |
<synopsis> |
|---|
| 428 |
CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional>, ...</optional>); |
|---|
| 429 |
</synopsis> |
|---|
| 430 |
La classe d'opérateurs identifie les opérateurs que l'index doit utiliser |
|---|
| 431 |
sur cette colonne. Par exemple, un index B-tree sur une colonne de type |
|---|
| 432 |
<type>int4</type> utiliserait la classe <literal>int4_ops</literal>; |
|---|
| 433 |
Cette classe d'opérateurs comprend des fonctions de comparaison pour les |
|---|
| 434 |
valeurs de type <type>int4</type>. |
|---|
| 435 |
En pratique, la classe d'opérateurs par défaut pour le type de données de la |
|---|
| 436 |
colonne est généralement suffisant. Les classes d'opérateurs sont utiles pour |
|---|
| 437 |
certains types de données, pour lesquels il pourrait y avoir plus d'un |
|---|
| 438 |
comportement utile de l'index. |
|---|
| 439 |
Par exemple, nous pourrions vouloir trier une donnée de type nombre complexe |
|---|
| 440 |
soit par sa valeur absolue, soit par sa partie entière. |
|---|
| 441 |
Nous pourrions le faire en définissant deux classes d'opérateurs pour ce |
|---|
| 442 |
type de données et en sélectionnant la bonne classe en créant l'index. |
|---|
| 443 |
</para> |
|---|
| 444 |
|
|---|
| 445 |
<para> |
|---|
| 446 |
Il y a quelques classes d'opérateurs en plus des classes par défaut: |
|---|
| 447 |
|
|---|
| 448 |
<itemizedlist> |
|---|
| 449 |
<listitem> |
|---|
| 450 |
<para> |
|---|
| 451 |
Les classes d'opérateurs <literal>text_pattern_ops</literal>, |
|---|
| 452 |
<literal>varchar_pattern_ops</literal>, |
|---|
| 453 |
<literal>bpchar_pattern_ops</literal>, et |
|---|
| 454 |
<literal>name_pattern_ops</literal> supportent les index B-tree sur |
|---|
| 455 |
les types <type>text</type>, <type>varchar</type>, |
|---|
| 456 |
<type>char</type>, et <type>name</type>, respectivement. |
|---|
| 457 |
La différence avec les classes d'opérateurs ordinaires est que |
|---|
| 458 |
les valeurs sont comparées strictement caractère par caractère plutôt |
|---|
| 459 |
que suivant les règles de tri spécifiques à la localisation. |
|---|
| 460 |
Cela rend ces index utilisables pour des requêtes qui utilisent des |
|---|
| 461 |
recherches sur des motifs (<literal>LIKE</literal> ou des expressions |
|---|
| 462 |
régulières POSIX) si le serveur n'utilise pas la localisation standard |
|---|
| 463 |
<quote>C</quote>. |
|---|
| 464 |
Par exemple, on pourrait indexer une colonne <type>varchar</type> |
|---|
| 465 |
comme ceci: |
|---|
| 466 |
<programlisting> |
|---|
| 467 |
CREATE INDEX test_index ON test_table (col varchar_pattern_ops); |
|---|
| 468 |
</programlisting> |
|---|
| 469 |
Si vous utilisez la localisation C, vous pouvez à la place créer |
|---|
| 470 |
un index avec la classe d'opérateurs par défaut, qui sera utilisable |
|---|
| 471 |
pour les recherches de motifs. |
|---|
| 472 |
Notez aussi qu'il faut créer un index avec la classe d'opérateurs par |
|---|
| 473 |
défaut si vous voulez que les requêtes qui utilisent une comparaison |
|---|
| 474 |
ordinaire utilisent un index. De telles requêtes ne peuvent pas utiliser |
|---|
| 475 |
les classes d'opérateurs |
|---|
| 476 |
<literal><replaceable>xxx</replaceable>_pattern_ops</literal>. |
|---|
| 477 |
Il est possible de créer plusieurs index sur la même colonne |
|---|
| 478 |
avec différentes classes d'opérateurs. |
|---|
| 479 |
</para> |
|---|
| 480 |
</listitem> |
|---|
| 481 |
</itemizedlist> |
|---|
| 482 |
</para> |
|---|
| 483 |
|
|---|
| 484 |
<para> |
|---|
| 485 |
Les requêtes suivantes montrent toutes les classes d'opérateurs prédéfinies: |
|---|
| 486 |
|
|---|
| 487 |
<programlisting> |
|---|
| 488 |
SELECT am.amname AS index_method, |
|---|
| 489 |
opc.opcname AS opclass_name |
|---|
| 490 |
FROM pg_am am, pg_opclass opc |
|---|
| 491 |
WHERE opc.opcamid = am.oid |
|---|
| 492 |
ORDER BY index_method, opclass_name; |
|---|
| 493 |
</programlisting> |
|---|
| 494 |
|
|---|
| 495 |
Elle peut être étendue pour montrer tous les opérateurs inclus dans chaque classe: |
|---|
| 496 |
|
|---|
| 497 |
<programlisting> |
|---|
| 498 |
SELECT am.amname AS index_method, |
|---|
| 499 |
opc.opcname AS opclass_name, |
|---|
| 500 |
opr.oprname AS opclass_operator |
|---|
| 501 |
FROM pg_am am, pg_opclass opc, pg_amop amop, pg_operator opr |
|---|
| 502 |
WHERE opc.opcamid = am.oid AND |
|---|
| 503 |
amop.amopclaid = opc.oid AND |
|---|
| 504 |
amop.amopopr = opr.oid |
|---|
| 505 |
ORDER BY index_method, opclass_name, opclass_operator; |
|---|
| 506 |
</programlisting> |
|---|
| 507 |
</para> |
|---|
| 508 |
</sect1> |
|---|
| 509 |
|
|---|
| 510 |
|
|---|
| 511 |
<sect1 id="indexes-partial"> |
|---|
| 512 |
<title>Index partiels</title> |
|---|
| 513 |
|
|---|
| 514 |
<indexterm zone="indexes-partial"> |
|---|
| 515 |
<primary>index</primary> |
|---|
| 516 |
<secondary>partiel</secondary> |
|---|
| 517 |
</indexterm> |
|---|
| 518 |
|
|---|
| 519 |
<para> |
|---|
| 520 |
Un <firstterm>index partiel</firstterm> est un index construit sur un |
|---|
| 521 |
sous-ensemble d'une table; le sous-ensemble est défini par une expression |
|---|
| 522 |
conditionnelle (appelée le <firstterm>prédicat</firstterm> de l'index |
|---|
| 523 |
partiel). L'index ne contient des entrées que pour les lignes de la |
|---|
| 524 |
table qui satisfont au prédicat. |
|---|
| 525 |
</para> |
|---|
| 526 |
|
|---|
| 527 |
<para> |
|---|
| 528 |
L'usage principal des index partiels est d'éviter d'indexer les valeurs |
|---|
| 529 |
trop courantes. Comme une requête qui fait des recherches sur une valeur |
|---|
| 530 |
trop courante (qui correspond à plus de quelques pour-cent des lignes) |
|---|
| 531 |
n'utilisera pas cet index de toute façon, il ne sert à rien de garder |
|---|
| 532 |
ces lignes dans l'index. |
|---|
| 533 |
Cela réduit la taille de l'index, ce qui accélère les requêtes qui |
|---|
| 534 |
l'utilisent. |
|---|
| 535 |
Cela accélère aussi beaucoup d'opérations de mise à jour de la table car |
|---|
| 536 |
l'index n'a pas besoin d'être mis à jour à chaque fois. |
|---|
| 537 |
<xref linkend="indexes-partial-ex1"> montre une application possible de |
|---|
| 538 |
cette idée. |
|---|
| 539 |
</para> |
|---|
| 540 |
|
|---|
| 541 |
<example id="indexes-partial-ex1"> |
|---|
| 542 |
<title>Mettre en place un index partiel pour exclure les valeurs courantes</title> |
|---|
| 543 |
|
|---|
| 544 |
<para> |
|---|
| 545 |
Supposons que vous enregistrez un journal d'accès à un serveur web dans une |
|---|
| 546 |
base de données. |
|---|
| 547 |
La plupart des accès proviennent de classes d'adresses IP internes à votre |
|---|
| 548 |
organisation, mais certaines viennent d'ailleurs (disons des employés connectés |
|---|
| 549 |
par modem). |
|---|
| 550 |
Si vos recherches sur des adresses IP concernent essentiellement les accès |
|---|
| 551 |
extérieures, vous n'avez probablement pas besoin d'indexer les classes |
|---|
| 552 |
d'adresses IP qui correspondent au sous-réseau de votre organisation. |
|---|
| 553 |
|
|---|
| 554 |
</para> |
|---|
| 555 |
|
|---|
| 556 |
<para> |
|---|
| 557 |
Supposons que la table soit comme ceci: |
|---|
| 558 |
<programlisting> |
|---|
| 559 |
CREATE TABLE access_log ( |
|---|
| 560 |
url varchar, |
|---|
| 561 |
client_ip inet, |
|---|
| 562 |
... |
|---|
| 563 |
); |
|---|
| 564 |
</programlisting> |
|---|
| 565 |
</para> |
|---|
| 566 |
|
|---|
| 567 |
<para> |
|---|
| 568 |
Pour créer un index partiel qui corresponde à notre exemple, il faut utiliser |
|---|
| 569 |
une commande comme celle-ci: |
|---|
| 570 |
<programlisting> |
|---|
| 571 |
CREATE INDEX access_log_client_ip_ix ON access_log (client_ip) |
|---|
| 572 |
WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255'); |
|---|
| 573 |
</programlisting> |
|---|
| 574 |
</para> |
|---|
| 575 |
|
|---|
| 576 |
<para> |
|---|
| 577 |
Une requête typique qui peut utiliser cet index est: |
|---|
| 578 |
<programlisting> |
|---|
| 579 |
SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32'; |
|---|
| 580 |
</programlisting> |
|---|
| 581 |
Une requête qui ne peut pas l'utiliser est: |
|---|
| 582 |
<programlisting> |
|---|
| 583 |
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23'; |
|---|
| 584 |
</programlisting> |
|---|
| 585 |
</para> |
|---|
| 586 |
|
|---|
| 587 |
<para> |
|---|
| 588 |
Observez que cet type d'index partiel nécessite que les valeurs courantes |
|---|
| 589 |
soient prédéterminées. Si la distribution des valeurs est inhérente |
|---|
| 590 |
(du fait de la nature de l'application) et statique (ne changeant pas dans |
|---|
| 591 |
le temps), ce n'est pas trop difficile, mais si les valeurs courantes sont |
|---|
| 592 |
simplement dues au hasard, cela peut demander beaucoup de travail de maintenance. |
|---|
| 593 |
</para> |
|---|
| 594 |
</example> |
|---|
| 595 |
|
|---|
| 596 |
<para> |
|---|
| 597 |
Il est aussi possible d'exclure des valeurs de l'index qui ne correspondent |
|---|
| 598 |
pas aux requêtes courantes; ceci est montré dans <xref |
|---|
| 599 |
linkend="indexes-partial-ex2">. Cette méthode donne les mêmes avantages que |
|---|
| 600 |
la précédente, mais empêche les valeurs <quote>sans intérêt</quote> |
|---|
| 601 |
d'être accédées. Évidemment, mettre en place des index partiels pour |
|---|
| 602 |
ce genre de scénario nécessite beaucoup de soin et d'expérimentation. |
|---|
| 603 |
</para> |
|---|
| 604 |
|
|---|
| 605 |
<example id="indexes-partial-ex2"> |
|---|
| 606 |
<title>Mettre en place un index partiel pour exclure les valeurs inintéressantes</title> |
|---|
| 607 |
|
|---|
| 608 |
<para> |
|---|
| 609 |
Si vous avez une table qui contient des commandes facturées et des |
|---|
| 610 |
commandes non facturées, que les commandes non facturées ne prennent |
|---|
| 611 |
qu'une petite fraction de l'espace dans la table, et que ces commandes |
|---|
| 612 |
non facturées sont les plus accédées, alors vous pouvez améliorer les |
|---|
| 613 |
performances en créant un index limité aux lignes non facturées. |
|---|
| 614 |
La commande pour créer l'index ressemblerait à ceci: |
|---|
| 615 |
<programlisting> |
|---|
| 616 |
CREATE INDEX orders_unbilled_index ON orders (order_nr) |
|---|
| 617 |
WHERE billed is not true; |
|---|
| 618 |
</programlisting> |
|---|
| 619 |
</para> |
|---|
| 620 |
|
|---|
| 621 |
<para> |
|---|
| 622 |
La requête suivante utilisera probablement cet index: |
|---|
| 623 |
<programlisting> |
|---|
| 624 |
SELECT * FROM orders WHERE billed is not true AND order_nr < 10000; |
|---|
| 625 |
</programlisting> |
|---|
| 626 |
Néanmoins, l'index peut aussi être utilisé dans des requêtes qui |
|---|
| 627 |
n'utilisent pas <structfield>order_nr</structfield>, comme: |
|---|
| 628 |
<programlisting> |
|---|
| 629 |
SELECT * FROM orders WHERE billed is not true AND amount > 5000.00; |
|---|
| 630 |
</programlisting> |
|---|
| 631 |
Ceci n'est pas aussi efficace qu'un index partiel sur la colonne |
|---|
| 632 |
<structfield>amount</structfield>, car le système doit lire l'index en entier. |
|---|
| 633 |
Néanmoins, s'il y a assez peu de commandes non facturées, l'utilisation |
|---|
| 634 |
de cet index partiel pour trouver les commandes non facturées peut être |
|---|
| 635 |
efficace. |
|---|
| 636 |
</para> |
|---|
| 637 |
|
|---|
| 638 |
<para> |
|---|
| 639 |
Notez que cette requête ne peut pas utiliser cet index: |
|---|
| 640 |
<programlisting> |
|---|
| 641 |
SELECT * FROM orders WHERE order_nr = 3501; |
|---|
| 642 |
</programlisting> |
|---|
| 643 |
La commande 3501 peut faire partie des commandes facturées ou |
|---|
| 644 |
bien des commandes non facturées. |
|---|
| 645 |
</para> |
|---|
| 646 |
</example> |
|---|
| 647 |
|
|---|
| 648 |
<para> |
|---|
| 649 |
<xref linkend="indexes-partial-ex2"> illustre aussi le fait que la colonne |
|---|
| 650 |
indexée et la colonne utilisée dans le prédicat ne sont pas nécessairement |
|---|
| 651 |
les mêmes. <productname>PostgreSQL</productname> supporte tous les |
|---|
| 652 |
prédicats sur les index partiels, tant que ceux-ci ne portent que sur des |
|---|
| 653 |
champs de la table indexée. Néanmoins, il faut se rappeler le prédicat doit |
|---|
| 654 |
correspondre aux conditions utilisées dans les requêtes qui sont supposées |
|---|
| 655 |
profiter de l'index. Pour être précis, un index partiel ne peut être utilisé |
|---|
| 656 |
pour une requête que si le système peut reconnaître que la clause |
|---|
| 657 |
<literal>WHERE</literal> de la requête implique mathématiquement le prédicat de |
|---|
| 658 |
l'index. |
|---|
| 659 |
<productname>PostgreSQL</productname> n'a pas de méthode sophistiquée de |
|---|
| 660 |
démonstration de théorème pour reconnaître que des expressions apparemment |
|---|
| 661 |
différentes sont mathématiquement équivalentes. (Non seulement une telle |
|---|
| 662 |
méthode générale de démonstration serait extrêmement complexe à créer, |
|---|
| 663 |
mais en plus elle serait probablement trop lente pour être d'une quelconque |
|---|
| 664 |
utilité.) |
|---|
| 665 |
Le système peut reconnaître des implications d'inégalités simples, par |
|---|
| 666 |
exemple <quote>x < 1</quote> implique <quote>x < 2</quote>; sinon, |
|---|
| 667 |
la condition du prédicat doit correspondre exactement à une partie de la |
|---|
| 668 |
clause <literal>WHERE</literal> de la requête, sans quoi l'index ne sera pas |
|---|
| 669 |
considéré comme utilisable. |
|---|
| 670 |
</para> |
|---|
| 671 |
|
|---|
| 672 |
<para> |
|---|
| 673 |
Le troisième usage possible des index partiels ne nécessite pas que |
|---|
| 674 |
l'index soit utilisé dans des requêtes. L'idée ici est de créer un index |
|---|
| 675 |
unique sur un sous-ensemble de la table, comme dans <xref |
|---|
| 676 |
linkend="indexes-partial-ex3">. Ceci permet de mettre en place une unicité |
|---|
| 677 |
parmi le sous-ensemble des lignes de la table qui satisfont au prédicat, |
|---|
| 678 |
sans contraindre les lignes qui n'y satisfont pas. |
|---|
| 679 |
</para> |
|---|
| 680 |
|
|---|
| 681 |
<example id="indexes-partial-ex3"> |
|---|
| 682 |
<title>Mettre en place un index unique partiel</title> |
|---|
| 683 |
|
|---|
| 684 |
<para> |
|---|
| 685 |
Supposons que nous ayons une table qui décrive des résultats de tests. |
|---|
| 686 |
Nous voulons nous assurer qu'il n'y a qu'une seule entrée |
|---|
| 687 |
<quote>succès</quote> (success) pour chaque combinaison de sujet (subject) et de |
|---|
| 688 |
résultat (target), mais il peut y avoir un nombre quelconque d'entrées |
|---|
| 689 |
<quote>echec</quote>. Voici une façon de le faire. |
|---|
| 690 |
<programlisting> |
|---|
| 691 |
CREATE TABLE tests ( |
|---|
| 692 |
subject text, |
|---|
| 693 |
target text, |
|---|
| 694 |
success boolean, |
|---|
| 695 |
... |
|---|
| 696 |
); |
|---|
| 697 |
|
|---|
| 698 |
CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) |
|---|
| 699 |
WHERE success; |
|---|
| 700 |
</programlisting> |
|---|
| 701 |
C'est une méthode très efficace pour le faire quand il y a peu de tests |
|---|
| 702 |
réussis et beaucoup de tests en échec. |
|---|
| 703 |
</para> |
|---|
| 704 |
</example> |
|---|
| 705 |
|
|---|
| 706 |
<para> |
|---|
| 707 |
Enfin, un index partiel peut aussi être utilisé pour passer outre aux |
|---|
| 708 |
choix de plan d'exécution de requête du système. |
|---|
| 709 |
Il peut arriver avec certains jeux de données particuliers que le |
|---|
| 710 |
système utilise un index alors qu'il ne devrait vraiment pas le faire. |
|---|
| 711 |
Dans ce cas, on peut mettre en place l'index de telle faon qu'il ne soit |
|---|
| 712 |
pas utilisé pour la requête qui pose problème. |
|---|
| 713 |
Normalement, <productname>PostgreSQL</productname> fait des choix d'usage d'index |
|---|
| 714 |
raisonnables. Par exemple, il les évite pour rechercher les valeurs communes, |
|---|
| 715 |
si bien que l'exemple précédent n'économise que la taille de l'index, il |
|---|
| 716 |
n'est en fait pas nécessaire pour éviter l'usage de l'index. |
|---|
| 717 |
En fait, les choix de plan d'exécution grossièrement incorrects doivent |
|---|
| 718 |
être traités comme des bogues, et être transmis à l'équipe de développement. |
|---|
| 719 |
</para> |
|---|
| 720 |
|
|---|
| 721 |
<para> |
|---|
| 722 |
Gardez à l'esprit que mettre en place un index partiel indique que vous |
|---|
| 723 |
connaissez vos données au moins aussi bien que l'analyseur de requêtes, |
|---|
| 724 |
et en particulier que vous savez quand un index peut être profitable. |
|---|
| 725 |
Une telle connaissance nécessite de l'expérience et une bonne |
|---|
| 726 |
compréhension du fonctionnement des index de <productname>PostgreSQL</productname>. |
|---|
| 727 |
Dans la plupart des cas, les index partiels ne représentent pas un |
|---|
| 728 |
gros gain par rapport aux index classiques. |
|---|
| 729 |
</para> |
|---|
| 730 |
|
|---|
| 731 |
<para> |
|---|
| 732 |
Vous trouverez plus d'informations sur les index partiels |
|---|
| 733 |
en lisant <xref linkend="STON89b">, <xref linkend="OLSON93">, |
|---|
| 734 |
et <xref linkend="SESHADRI95">. |
|---|
| 735 |
</para> |
|---|
| 736 |
</sect1> |
|---|
| 737 |
|
|---|
| 738 |
<sect1 id="indexes-examine"> |
|---|
| 739 |
<title>Examiner l'usage des index</title> |
|---|
| 740 |
|
|---|
| 741 |
<indexterm zone="indexes-examine"> |
|---|
| 742 |
<primary>index</primary> |
|---|
| 743 |
<secondary>examiner l'usage</secondary> |
|---|
| 744 |
</indexterm> |
|---|
| 745 |
|
|---|
| 746 |
<para> |
|---|
| 747 |
Bien que les index de <productname>PostgreSQL</productname> n'aient pas besoin |
|---|
| 748 |
de maintenance ni d'optimisation, il est important de s'assurer que les |
|---|
| 749 |
index sont effectivement utilisés sur un système en production. |
|---|
| 750 |
On vérifie l'utilisation d'un index pour une requête particulière avec |
|---|
| 751 |
la commande <xref linkend="sql-explain" endterm="sql-explain-title">. |
|---|
| 752 |
Son utilisation dans notre cas est expliquée dans |
|---|
| 753 |
<xref linkend="using-explain">. |
|---|
| 754 |
Il est aussi possible de rassembler des statistiques globales |
|---|
| 755 |
sur l'utilisation des index sur un serveur en cours de fonctionnement, |
|---|
| 756 |
comme décrit dans <xref linkend="monitoring-stats">. |
|---|
| 757 |
</para> |
|---|
| 758 |
|
|---|
| 759 |
<para> |
|---|
| 760 |
Il est difficile de donner une procédure générale pour déterminer |
|---|
| 761 |
quels index doivent être créés. Plusieurs cas typiques ont été |
|---|
| 762 |
cités dans les exemples précédents. |
|---|
| 763 |
Une bonne dose d'expérimentation sera nécessaire dans de nombreux cas. |
|---|
| 764 |
Le reste de cette section donne quelques pistes. |
|---|
| 765 |
</para> |
|---|
| 766 |
|
|---|
| 767 |
<itemizedlist> |
|---|
| 768 |
<listitem> |
|---|
| 769 |
<para> |
|---|
| 770 |
La première chose à faire est de lancer |
|---|
| 771 |
<xref linkend="sql-analyze" endterm="sql-analyze-title">. |
|---|
| 772 |
Cette commande collecte les informations sur la distribution des |
|---|
| 773 |
valeurs dans la table. Cette information est nécessaire pour essayer de |
|---|
| 774 |
deviner le nombre lignes retournées par une requête. L'optimiseur |
|---|
| 775 |
de requêtes en a besoin pour donner des coûts réalistes aux différents |
|---|
| 776 |
plans de requêtes possibles. En l'absence de statistiques réelles, |
|---|
| 777 |
le système utilise quelques valeurs par défaut, qui ont toutes les chances |
|---|
| 778 |
d'être inadaptées. Examiner l'utilisation des index par une application |
|---|
| 779 |
sans avoir lancé <command>ANALYZE</command> préalablement est du coup |
|---|
| 780 |
une cause perdue. |
|---|
| 781 |
</para> |
|---|
| 782 |
</listitem> |
|---|
| 783 |
|
|---|
| 784 |
<listitem> |
|---|
| 785 |
<para> |
|---|
| 786 |
Utilisez des données réelles pour l'expérimentation. Utiliser des |
|---|
| 787 |
données de test pour mettre en place des index vous permettra |
|---|
| 788 |
de trouver les index dont vous avez besoin pour vos données de test, |
|---|
| 789 |
mais c'est tout. |
|---|
| 790 |
</para> |
|---|
| 791 |
|
|---|
| 792 |
<para> |
|---|
| 793 |
Il est particulièrement néfaste d'utiliser un jeu de données |
|---|
| 794 |
constitué en réduisant proportionnellement des données réelles. |
|---|
| 795 |
Alors qu'une requête sélectionnant 1000 lignes parmi 100000 pourrait |
|---|
| 796 |
utiliser un index, il est peu probable qu'une requête sélectionnant 1 ligne |
|---|
| 797 |
dans une table de 100 lignes le fasse, parce que les 100 lignes |
|---|
| 798 |
tiennent probablement dans une seule page sur le disque, et qu'il n'y a |
|---|
| 799 |
aucun plan d'exécution qui puisse aller plus vite que la lecture |
|---|
| 800 |
d'une seule page. |
|---|
| 801 |
</para> |
|---|
| 802 |
|
|---|
| 803 |
<para> |
|---|
| 804 |
Soyez aussi vigilant en créant des données de test, ce qui est souvent |
|---|
| 805 |
inévitable quand l'application n'est pas encore en production. |
|---|
| 806 |
Les valeurs qui sont très similaires, complètement aléatoire, ou |
|---|
| 807 |
insérées déjà triées peuvent modifier la distribution des données et |
|---|
| 808 |
fausser les statistiques. |
|---|
| 809 |
</para> |
|---|
| 810 |
</listitem> |
|---|
| 811 |
|
|---|
| 812 |
<listitem> |
|---|
| 813 |
<para> |
|---|
| 814 |
Quand les index ne sont pas utilisés, il peut être utile pour |
|---|
| 815 |
les tests de forcer leur utilisation. Certains paramètres d'exécution |
|---|
| 816 |
du serveur peuvent interdire certains types de plans (décrits dans |
|---|
| 817 |
<xref linkend="runtime-config">). |
|---|
| 818 |
Par exemple, en interdisant les lectures séquentielles de tables |
|---|
| 819 |
<varname>enable_seqscan</varname>) et les jointures à boucles imbriquées |
|---|
| 820 |
(<varname>enable_nestloop</varname>), qui sont les deux plans les plus |
|---|
| 821 |
basiques, on forcera le système à utiliser un plan différent. |
|---|
| 822 |
Si le système continue néanmoins à choisir une lecture séquentielle |
|---|
| 823 |
ou une jointure à boucles imbriquées, alors il y a probablement |
|---|
| 824 |
un problème plus fondamental qui empêche l'utilisation de l'index, |
|---|
| 825 |
par exemple que la condition ne correspond pas à l'index. |
|---|
| 826 |
(Les sections précédentes expliquent quelles sortes de requêtes peuvent |
|---|
| 827 |
utiliser quelles sortes d'index.) |
|---|
| 828 |
</para> |
|---|
| 829 |
</listitem> |
|---|
| 830 |
|
|---|
| 831 |
<listitem> |
|---|
| 832 |
<para> |
|---|
| 833 |
Si l'index est effectivement utilisé en forçant son utilisation, |
|---|
| 834 |
alors il y a deux possibilités: Soit le système a raison et |
|---|
| 835 |
l'utilisation de l'index est effectivement inappropriée, soit les |
|---|
| 836 |
coûts estimés des plans de requêtes ne reflètent pas la réalité. |
|---|
| 837 |
Il faut alors comparer la durée de la requête avec et sans |
|---|
| 838 |
index. La commande <command>EXPLAIN ANALYZE</command> peut être |
|---|
| 839 |
utile pour cela. |
|---|
| 840 |
</para> |
|---|
| 841 |
</listitem> |
|---|
| 842 |
|
|---|
| 843 |
<listitem> |
|---|
| 844 |
<para> |
|---|
| 845 |
S'il apparaît que les estimations de coûts sont fausses, il y a |
|---|
| 846 |
de nouveau deux possibilités. Le coût total est calculé à partir du |
|---|
| 847 |
coût par ligne de chaque nœud du plan, multiplié par l'estimation de |
|---|
| 848 |
sélectivité du nœud de plan. |
|---|
| 849 |
Le coût des nœuds de plan peut être optimisé avec |
|---|
| 850 |
des paramètres d'exécution (décrits dans <xref linkend="runtime-config">). |
|---|
| 851 |
Une estimation de sélectivité inadaptée est due à des statistiques |
|---|
| 852 |
insuffisantes. Il est peut être possible de les améliorer en |
|---|
| 853 |
optimisant les paramètres de collecte de statistiques. |
|---|
| 854 |
Voir <xref linkend="sql-altertable" endterm="sql-altertable-title">). |
|---|
| 855 |
</para> |
|---|
| 856 |
|
|---|
| 857 |
<para> |
|---|
| 858 |
Si vous n'arrivez pas à ajuster les coûts pour qu'ils représentent |
|---|
| 859 |
mieux la réalité, alors vous devrez forcer l'utilisation de l'index |
|---|
| 860 |
explicitement. |
|---|
| 861 |
Vous pouvez aussi, si vous le voulez, contacter les développeurs de |
|---|
| 862 |
<productname>PostgreSQL</productname> afin qu'ils examinent le problème. |
|---|
| 863 |
</para> |
|---|
| 864 |
</listitem> |
|---|
| 865 |
</itemizedlist> |
|---|
| 866 |
</sect1> |
|---|
| 867 |
</chapter> |
|---|
| 868 |
|
|---|
| 869 |
<!-- Keep this comment at the end of the file |
|---|
| 870 |
Local variables: |
|---|
| 871 |
mode:sgml |
|---|
| 872 |
sgml-omittag:nil |
|---|
| 873 |
sgml-shorttag:t |
|---|
| 874 |
sgml-minimize-attributes:nil |
|---|
| 875 |
sgml-always-quote-attributes:t |
|---|
| 876 |
sgml-indent-step:1 |
|---|
| 877 |
sgml-indent-data:t |
|---|
| 878 |
sgml-parent-document:nil |
|---|
| 879 |
sgml-default-dtd-file:"./reference.ced" |
|---|
| 880 |
sgml-exposed-tags:nil |
|---|
| 881 |
sgml-local-catalogs:("/usr/lib/sgml/catalog") |
|---|
| 882 |
sgml-local-ecat-files:nil |
|---|
| 883 |
End: |
|---|
| 884 |
--> |
|---|
| 885 |
|
|---|