root/traduc/branches/bv747/manuel/indices.sgml

Revision 13, 35.4 kB (checked in by gleu, 3 years ago)

Suite de l'import pour le passage CVS à SVN.

Line 
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>&lt;</literal></member>
131     <member><literal>&lt;=</literal></member>
132     <member><literal>=</literal></member>
133     <member><literal>&gt;=</literal></member>
134     <member><literal>&gt;</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>&lt;&lt;</literal></member>
171     <member><literal>&amp;&lt;</literal></member>
172     <member><literal>&amp;&gt;</literal></member>
173     <member><literal>&gt;&gt;</literal></member>
174     <member><literal>@</literal></member>
175     <member><literal>~=</literal></member>
176     <member><literal>&amp;&amp;</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 &lt; 1</quote> implique <quote>x &lt; 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&oelig;ud du plan, multiplié par l'estimation de
848      sélectivité du n&oelig;ud de plan.
849      Le coût des n&oelig;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
Note: See TracBrowser for help on using the browser.