forked from gleu/pgdocs_fr
/
storage.xml
868 lines (770 loc) · 36 KB
/
storage.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="storage">
<title>Stockage physique de la base de données</title>
<para>
Ce chapitre fournit un aperçu du format de stockage physique utilisé par les
bases de données <productname>PostgreSQL</productname>.
</para>
<sect1 id="storage-file-layout">
<title>Emplacement des fichiers de la base de données</title>
<para>
Cette section décrit le format de stockage au niveau des fichiers et
répertoires.
</para>
<para>
Toutes les données nécessaires à un groupe de bases de données sont stockées
dans le répertoire data du groupe, habituellement référencé en tant que
<varname>PGDATA</varname> (d'après le nom de la variable d'environnement qui peut
être utilisé pour le définir). Un emplacement courant pour <varname>PGDATA</varname>
est <filename>/var/lib/pgsql/data</filename>. Plusieurs groupes, gérés par différentes
instances du serveur, peuvent exister sur la même machine.
</para>
<para>
Le répertoire <varname>PGDATA</varname> contient plusieurs sous-répertoires et
fichiers de contrôle, comme indiqué dans le <xref linkend="pgdata-contents-table"/>.
En plus de ces éléments requis, les fichiers
de configuration du groupe, <filename>postgresql.conf</filename>,
<filename>pg_hba.conf</filename> et <filename>pg_ident.conf</filename> sont
traditionnellement stockés dans <varname>PGDATA</varname> (bien qu'il soit possible de
les conserver ailleurs à partir de la version 8.0 de
<productname>PostgreSQL</productname>).
</para>
<table tocentry="1" id="pgdata-contents-table">
<title>Contenu de <varname>PGDATA</varname></title>
<tgroup cols="2">
<colspec colnum="1" colwidth="0.5*"/>
<colspec colnum="2" colwidth="1.5*"/>
<thead>
<row>
<entry>Élément</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><filename>PG_VERSION</filename></entry>
<entry>Un fichier contenant le numéro de version majeur de
<productname>PostgreSQL</productname></entry>
</row>
<row>
<entry><filename>base</filename></entry>
<entry>Sous-répertoire contenant les sous-répertoires par base de
données</entry>
</row>
<row>
<entry><filename>global</filename></entry>
<entry>Sous-répertoire contenant les tables communes au groupe, telles que
<structname>pg_database</structname></entry>
</row>
<row>
<entry><filename>pg_clog</filename></entry>
<entry>Sous-répertoire contenant les données d'état de validation des
transactions</entry>
</row>
<row>
<entry><filename>pg_multixact</filename></entry>
<entry>Sous-répertoire contenant des données sur l'état des
multi-transactions (utilisé pour les verrous de lignes partagées)</entry>
</row>
<row>
<entry><filename>pg_stat_tmp</filename></entry>
<entry>Sous-répertoire contenant les fichiers temporaires pour le sous-système
des statistiques</entry>
</row>
<row>
<entry><filename>pg_subtrans</filename></entry>
<entry>Sous-répertoire contenant les données d'états des
sous-transaction</entry>
</row>
<row>
<entry><filename>pg_tblspc</filename></entry>
<entry>Sous-répertoire contenant les liens symboliques vers les espaces
logiques</entry>
</row>
<row>
<entry><filename>pg_twophase</filename></entry>
<entry>Sous-répertoire contenant les fichiers d'état pour les transactions
préparées</entry>
</row>
<row>
<entry><filename>pg_xlog</filename></entry>
<entry>Sous-répertoire contenant les fichiers WAL (Write Ahead Log)</entry>
</row>
<row>
<entry><filename>postmaster.opts</filename></entry>
<entry>Un fichier enregistrant les options en ligne de commande avec
lesquelles le serveur a été lancé la dernière fois</entry>
</row>
<row>
<entry><filename>postmaster.pid</filename></entry>
<entry>Un fichier verrou enregistrant le PID courant du serveur et l'identifiant
du segment de mémoire partagé (absent après l'arrêt du serveur)</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Pour chaque base de données dans le groupe, il existe un sous-répertoire dans
<varname>PGDATA</varname><filename>/base</filename>, nommé d'après l'OID de la base de données
dans <structname>pg_database</structname>. Ce sous-répertoire est l'emplacement par
défaut pour les fichiers de la base de données; en particulier, ses
catalogues système sont stockés ici.
</para>
<para>
Chaque table et index sont stockés dans un fichier séparé, nommé d'après le
numéro <firstterm>filenode</firstterm> de la table ou de l'index, lequel se
trouve dans <structname>pg_class</structname>.<structfield>relfilenode</structfield>.
En plus du fichier principal (appelé aussi « main fork »), chaque
table et index a une <firstterm>carte des espaces libres</firstterm> (voir
<xref linkend="storage-fsm"/>), qui stocke des informations sur les emplacements
libres dans la relation. La carte des espace libre est stockée dans un fichier
nommé par le numéro relfilenode à qui est ajouté le suffixe
<literal>_fsm</literal>. Les tables ont aussi un fichier pour la <firstterm>carte de
visibilité</firstterm>, de suffixe <literal>_vm</literal>, pour tracer les pages connues
pour n'avoir aucune ligne morte.
La carte de visibilité est décrite plus bas dans <xref linkend="storage-vm"/>.
</para>
<caution>
<para>
Notez que, bien que le filenode de la table correspond souvent à son OID,
cela n'est <emphasis>pas</emphasis> nécessairement le cas; certaines
opérations, comme <command>TRUNCATE</command>, <command>REINDEX</command>,
<command>CLUSTER</command> et quelques formes d'<command>ALTER TABLE</command>, peuvent
modifier le filenode tout en préservant l'OID. Évitez de supposer que filenode
et OID sont identiques.
</para>
</caution>
<para>
Quand une table ou un index dépasse 1 Go, il est divisé en
<firstterm>segments</firstterm> d'un Go. Le nom du fichier du premier
segment est identique au filenode ; les segments suivants sont nommés
filenode.1, filenode.2, etc. Cette disposition évite des problèmes sur les
plateformes qui ont des limitations sur les tailles des fichiers.
(Actuellement, 1 Go est la taille du segment par défaut. Cette taille est
ajustable en utilisant l'option <option>--with-segsize</option> pour configure
avant de construire <productname>PostgreSQL</productname>.)
En principe, les fichiers de la carte des espaces libres et de la carte de
visibilité pourraient aussi nécessiter plusieurs segments, bien qu'il y a
peu de chance que cela arrive réellement.
Le contenu des tables et des index est discuté plus
en détails dans <xref linkend="storage-page-layout"/>.
</para>
<para>
Une table contenant des colonnes avec des entrées potentiellement volumineuses
aura une table <firstterm>TOAST</firstterm> associée, qui est
utilisée pour le stockage de valeurs de champs trop importantes pour
conserver des lignes adéquates.
<structname>pg_class</structname>.<structfield>reltoastrelid</structfield> établit un lien entre
une table et sa table <acronym>TOAST</acronym>, si elle existe. Voir <xref
linkend="storage-toast"/> pour plus d'informations.
</para>
<para>
Les tablespaces rendent ce scénario plus compliqués. Chaque espace
logique défini par l'utilisateur contient un lien symbolique dans le répertoire
<varname>PGDATA</varname><filename>/pg_tblspc</filename>, pointant vers le répertoire physique
du tablespace (comme spécifié dans sa commande <command>CREATE
TABLESPACE</command>). Le lien symbolique est nommé d'après l'OID du tablespace.
À l'intérieur du répertoire du tablespace, il existe un sous-répertoire
pour chacune des bases de données contenant des éléments dans ce tablespace. Ce
sous-répertoire est nommé d'après l'OID de la base. Les tables de
ce répertoire suivent le schéma de nommage des filenodes. Le tablespace
<literal>pg_default</literal> n'est pas accédé via <filename>pg_tblspc</filename> mais
correspond à <varname>PGDATA</varname><filename>/base</filename>. De façon similaire,
le tablespace <literal>pg_global</literal> n'est pas accédé via
<filename>pg_tblspc</filename> mais correspond à <varname>PGDATA</varname><filename>/global</filename>.
</para>
<para>
Les fichiers temporaires (pour des opérations comme le tri de plus de données
que ce que la mémoire peut contenir) sont créés à l'intérieur de <varname>PGDATA</varname><filename>/base/pgsql_tmp</filename>,
ou dans un sous-répertoire <filename>pgsql_tmp</filename> du répertoire du
tablespace si un tablespace autre que <literal>pg_default</literal> est
indiqué pour eux. Le nom du fichier temporaire est de la forme
<filename>pgsql_tmp<replaceable>PPP</replaceable>.<replaceable>NNN</replaceable></filename>,
où <replaceable>PPP</replaceable> est le PID du serveur propriétaire et
<replaceable>NNN</replaceable> distingue les différents fichiers temporaires de ce
serveur.
</para>
</sect1>
<sect1 id="storage-toast">
<title>TOAST</title>
<indexterm>
<primary>TOAST</primary>
</indexterm>
<indexterm><primary>sliced bread</primary><see>TOAST</see></indexterm>
<para>
Cette section fournit un aperçu de <acronym>TOAST</acronym> (<foreignphrase>The
Oversized-Attribute Storage Technique</foreignphrase>, la technique de
stockage des attributs trop grands).
</para>
<para>
Puisque <productname>PostgreSQL</productname> utilise une taille de page fixe
(habituellement 8 Ko) et n'autorise pas qu'une ligne s'étende sur plusieurs
pages. Du coup, il n'est pas possible de stocker de grandes valeurs directement
dans les champs. Pour dépasser cette limitation, les valeurs de champ
volumineuses sont compressées et/ou divisées en plusieurs lignes physiques. Ceci
survient de façon transparente pour l'utilisateur, avec seulement un petit
impact sur le code du serveur. Cette technique est connu sous l'acronyme
affectueux de <acronym>TOAST</acronym> (ou <quote>the best thing since sliced
bread</quote>).
</para>
<para>
Seuls certains types de données supportent <acronym>TOAST</acronym> — il n'est
pas nécessaire d'imposer cette surcharge sur les types de données qui ne
produisent pas de gros volumes. Pour supporter
<acronym>TOAST</acronym>, un type de données doit avoir une représentation
(<firstterm>varlena</firstterm>) à longueur variable, dans laquelle les 32 premiers bits
contiennent la longueur totale de la valeur en octets (ceci incluant la
longueur elle-même). <acronym>TOAST</acronym> n'a aucune contrainte supplémentaire
sur la représentation. Toutes les fonctions niveau C qui gèrent un type données
supportant <acronym>TOAST</acronym> doivent faire attention à gérer les valeurs en
entrée <acronym>TOAST</acronym>ées. (Ceci se fait normalement en appelant
<function>PG_DETOAST_DATUM</function> avant de faire quoi que ce soit avec une valeur
en entrée; mais dans certains cas, des approches plus efficaces sont possibles.)
</para>
<para>
<acronym>TOAST</acronym> récupère deux bits du mot contenant la longueur
d'un varlena (ceux de poids fort sur les machines big-endian, ceux de poids
faible sur les machines little-endian), limitant du coup la taille logique
de toute valeur d'un type de données <acronym>TOAST</acronym> à 1 Go
(2<superscript>30</superscript> - 1 octets). Quand les deux bits sont à
zéro, la valeur est une valeur non <acronym>TOAST</acronym>é du type de
données et les bits restants dans le mot contenant la longueur indique
la taille total du datum (incluant ce mot) en octets. Quand le bit de poids
fort (ou de poids faible) est à un, la valeur a un en-tête de seulement
un octet alors qu'un en-tête normal en fait quatre. Les bits restants
donne la taille total du datum (incluant ce mot) en octets. Il reste un
cas spécial : si les bits restants sont tous à zéro (ce qui est
impossible étant donné que le mot indiquant la longueur est inclut dans
la taille), la valeur est un pointeur vers une donnée stockée dans une table
TOAST séparée (la taille d'un pointeur TOAST est indiquée dans le second
octet du datum). Les valeurs dont l'en-tête fait un seul octet ne sont pas
alignées sur une limite particulière. Enfin, quand le bit de poids fort
(ou de poids faible) est supprimé mais que le bit adjacent vaut un, le
contenu du datum est compressé et doit être décompresser avant utilisation.
Dans ce cas, les bits restants du mot contenant la longueur indiquent la
taille totale du datum compressé, pas celles des données au départ. Notez
que la compression est aussi possible pour les données de la table TOAST
mais l'en-tête varlena n'indique pas si c'est le cas — le contenu
du pointeur TOAST le précise.
</para>
<para>
Si une des colonnes d'une table est <acronym>TOAST</acronym>-able, la table disposera
d'une table <acronym>TOAST</acronym> associé, dont l'OID est stockée dans l'entrée
<structname>pg_class</structname>.<structfield>reltoastrelid</structfield> de la table. Les valeurs
<acronym>TOAST</acronym>ées hors-ligne sont conservées dans la table <acronym>TOAST</acronym>
comme décrit avec plus de détails ci-dessous.
</para>
<para>
La technique de compression utilisée est un simple et rapide membre de la
famille des techniques de compression LZ. Voir <filename>src/backend/utils/adt/pg_lzcompress.c</filename> pour les
détails.
</para>
<para>
Les valeurs hors-ligne sont divisées (après compression si nécessaire) en
morceaux d'au plus <literal>TOAST_MAX_CHUNK_SIZE</literal> octets (par défaut,
cette valeur est choisie pour que quatre morceaux de ligne tiennent sur une
page, d'où les 2000 octets). Chaque morceau est stocké comme une ligne séparée dans la table
<acronym>TOAST</acronym> de la table propriétaire. Chaque table <acronym>TOAST</acronym>
contient les colonnes <structfield>chunk_id</structfield> (un OID identifiant la valeur
<acronym>TOAST</acronym>ée particulière), <structfield>chunk_seq</structfield> (un numéro de
séquence pour le morceau de la valeur) et <structfield>chunk_data</structfield> (la donnée
réelle du morceau). Un index unique sur <structfield>chunk_id</structfield> et
<structfield>chunk_seq</structfield> offre une récupération rapide des valeurs. Un
pointeur datum représentant une valeur <acronym>TOAST</acronym>ée hors-ligne a par conséquent
besoin de stocker l'OID de la table <acronym>TOAST</acronym> dans laquelle chercher
et l'OID de la valeur spécifique (son <structfield>chunk_id</structfield>). Par commodité,
les pointeurs datums stockent aussi la taille logique du datum (taille
de la donnée originale non compressée) et la taille stockée réelle (différente
si la compression a été appliquée). À partir des octets d'en-tête varlena,
la taille totale d'un pointeur datum <acronym>TOAST</acronym> est par conséquent de 18 octets
quelque soit la taille réelle de la valeur représentée.
</para>
<para>
Le code <acronym>TOAST</acronym> est déclenché seulement quand une valeur de ligne
à stocker dans une table est plus grande que <symbol>TOAST_TUPLE_THRESHOLD</symbol> octets (habituellement
2 Ko). Le code <acronym>TOAST</acronym> compressera et/ou déplacera les valeurs
de champ hors la ligne jusqu'à ce que la valeur de la ligne soit plus petite que
<symbol>TOAST_TUPLE_TARGET</symbol> octets (habituellement là-aussi
2 Ko) ou que plus aucun gain ne puisse être réalisé.
Lors d'une opération UPDATE, les valeurs des champs non modifiées sont habituellement
préservées telles quelles ; donc un UPDATE sur une ligne avec des valeurs hors
ligne n'induit pas de coûts à cause de <acronym>TOAST</acronym> si aucune des valeurs
hors-ligne n'est modifiée.
</para>
<para>
Le code <acronym>TOAST</acronym> connaît quatre stratégies différentes pour stocker
les colonnes <acronym>TOAST</acronym>-ables :
<itemizedlist>
<listitem>
<para>
<literal>PLAIN</literal> empêche soit la compression soit le stockage
hors-ligne ; de plus, il désactive l'utilisation d'en-tête sur
un octet pour les types varlena. Ceci est la seule stratégie possible
pour les colonnes des types de données non
<acronym>TOAST</acronym>-ables.
</para>
</listitem>
<listitem>
<para>
<literal>EXTENDED</literal> permet à la fois la compression et le
stockage hors-ligne. Ceci est la valeur par défaut de la plupart des
types de données <acronym>TOAST</acronym>-ables. La compression sera tentée en
premier, ensuite le stockage hors-ligne si la ligne est toujours trop
grande.
</para>
</listitem>
<listitem>
<para>
<literal>EXTERNAL</literal> autorise le stockage hors-ligne mais pas la
compression. L'utilisation d'<literal>EXTERNAL</literal> rendra plus rapides les
opérations sur des sous-chaînes d'importantes colonnes de type
<type>text</type> et <type>bytea</type> (au dépens d'un
espace de stockage accrus) car ces opérations sont optimisées pour
récupérer seulement les parties requises de la valeur hors-ligne
lorsqu'elle n'est pas compressée.
</para>
</listitem>
<listitem>
<para>
<literal>MAIN</literal> autorise la compression mais pas le stockage
hors-ligne. (En réalité le stockage hors-ligne sera toujours réalisé
pour de telles colonnes mais seulement en dernier ressort s'il n'existe
aucune autre solution pour diminuer suffisamment la taille de la ligne.)
</para>
</listitem>
</itemizedlist>
Chaque type de données <acronym>TOAST</acronym>-able spécifie une stratégie par défaut
pour les colonnes de ce type de donnée, mais la stratégie pour une colonne d'une table
donnée peut être modifiée avec <command>ALTER TABLE SET STORAGE</command>.
</para>
<para>
Cette combinaison a de nombreux avantages comparés à une approche plus directe
comme autoriser le stockage des valeurs de lignes sur plusieurs pages. En
supposant que les requêtes sont habituellement qualifiées par comparaison avec
des valeurs de clé relativement petites, la grosse partie du travail de
l'exécuteur sera réalisée en utilisant l'entrée principale de la ligne. Les
grandes valeurs des attributs <acronym>TOAST</acronym>és seront seulement récupérées
(si elles sont sélectionnées) au moment où l'ensemble de résultats est
envoyé au client. Ainsi, la table principale est bien plus petite
et un plus grand nombre de ses lignes tiennent dans le cache du tampon partagé,
ce qui ne serait pas le cas sans aucun stockage hors-ligne.
Le tri l'utilise aussi, et les tris seront plus souvent réalisés entièrement
en mémoire. Un petit test a montré qu'une table contenant des pages HTML
typiques ainsi que leurs URL étaient stockées en à peu près la moitié de la
taille des données brutes en incluant la table <acronym>TOAST</acronym> et que la
table principale contenait moins de 10 % de la totalité des données (les
URL et quelques petites pages HTML). Il n'y avait pas de différence à l'exécution
en comparaison avec une table non <acronym>TOAST</acronym>ée, dans laquelle toutes les
pages HTLM avaient été coupées à 7 Ko pour tenir.
</para>
</sect1>
<sect1 id="storage-fsm">
<title>Carte des espaces libres</title>
<indexterm>
<primary>Free Space Map</primary>
</indexterm>
<indexterm><primary>FSM</primary><see>Free Space Map</see></indexterm>
<para>
Chaque table et index, en dehors des index hash, a une carte des espaces libres
(appelée aussi <acronym>FSM</acronym>, acronyme de <foreignphrase>Free Space
Map</foreignphrase>) pour conserver le trace des emplacements disponibles dans
la relation. Elle est stockée dans un fichier séparé du fichier des données. Le
nom de fichier est le numéro relfilenode suivi du suffixe
<literal>_fsm</literal>. Par exemple, si le relfilenode d'une relation est
12345, la FSM est stockée dans un fichier appelé
<filename>12345_fsm</filename>, dans même répertoire que celui utilisé pour le
fichier des données.
</para>
<para>
La carte des espaces libres est organisée comme un arbre de pages
<acronym>FSM</acronym>. Les pages <acronym>FSM</acronym> de niveau bas stockent
l'espace libre disponible dans chaque page de la relation. Les niveaux
suppérieurs agrégent l'information des niveaux bas.
</para>
<para>
À l'intérieur de chaque page <acronym>FSM</acronym> se trouve un arbre binaire
stocké dans un tableau avec un octet par nœud. Chaque nœud final
représente une page de la relation, ou une page FSM de niveau bas. Dans chaque
nœud non final, la valeur la plus haute des valeurs enfants est stockée.
Du coup, la valeur maximum de tous les nœuds se trouve à la racine.
</para>
<para>
Voir <filename>src/backend/storage/freespace/README</filename> pour plus de
détails sur la façon dont la <acronym>FSM</acronym> est structurée, et comment
elle est mise à jour et recherchée. Le module contrib
<filename>contrib/pg_freespacemap</filename> peut être utilisé pour examiner
l'information stockée dans les cartes d'espace libre (voir <xref
linkend="pgfreespacemap"/>).
</para>
</sect1>
<sect1 id="storage-vm">
<title>Carte de visibilité</title>
<indexterm>
<primary>Carte de visibilité</primary>
</indexterm>
<indexterm><primary>VM</primary><see>Carte de visibilité</see></indexterm>
<para>
Chaque relation a une carte de visibilité (<acronym>VM</acronym> acronyme de
<foreignphrase>Visibility Map</foreignphrase>) pour garder trace des pages
contenant seulement des lignes connues pour être visibles par toutes les
transactions actives. Elle est stockée en dehors du fichier de données dans
un fichier séparé nommé suivant le numéro relfilenode de la relation, auquel
est ajouté le suffixe <literal>_vm</literal>. Par exemple, si le relfilenode
de la relation est 12345, la VM est stockée dans un fichier appelé
<filename>12345_vm</filename>, dans le même répertoire que celui du fichier
de données. Notez que les index n'ont pas de VM.
</para>
<para>
La carte de visibilité enregistre un bit par page. Un bit à 1 signifie
que toutes les lignes de la page sont visibles par toutes les transactions.
Cela signifie que le page ne contient pas de lignes nécessitant un VACUUM ;
dans le futur, cela pourra aussi être utilisé pour éviter de visiter la page
lors de vérifications de visibilité. Chaque fois qu'un bit est à 1, la condition
est vraie à coup sûr. Par contre, dans le cas contraire, la condition peut être
vraie comme fausse.
</para>
</sect1>
<sect1 id="storage-page-layout">
<title>Emplacement des pages de la base de données</title>
<para>
Cette section fournit un aperçu du format des pages utilisées par les tables et
index de <productname>PostgreSQL</productname>.<footnote>
<para>
En réalité, les méthodes d'accès par index n'ont pas besoin d'utiliser ce
format de page. Toutes les méthodes d'indexage existantes utilisent ce
format de base mais les données conservées dans les métapages des index
ne suivent habituellement pas les règles d'emplacement des éléments.
</para>
</footnote>
Les séquences et les tables <acronym>TOAST</acronym> tables sont formatées comme des
tables standards.
</para>
<para>
Dans l'explication qui suit, un <firstterm>octet</firstterm> contient huit
bits. De plus, le terme <firstterm>élément</firstterm> fait référence à une
valeur de données individuelle qui est stockée dans une page. Dans une table,
un élément est une ligne ; dans un index, un élément est une entrée
d'index.
</para>
<para>
Chaque table et index est stocké comme un tableau de <firstterm>pages</firstterm> d'une
taille fixe (habituellement 8 Ko, bien qu'une taille de page différente
peut être sélectionnée lors de la compilation du serveur). Dans une table,
toutes les pages sont logiquement équivalentes pour qu'un élément (ligne)
particulier puisse être stocké dans n'importe quelle page. Dans les index, la
première page est généralement réservée comme <firstterm>métapage</firstterm> contenant
des informations de contrôle, et il peut exister différents types de pages à
l'intérieur de l'index, suivant la méthode d'accès à l'index. Les tables ont
aussi une carte de visibilité dans un fichier de suffixe <literal>_vm</literal>,
pour tracer les pages dont on sait qu'elles ne contiennent pas de lignes mortes
et qui n'ont pas du coup besoin de VACUUM.
</para>
<para>
<xref linkend="page-table"/> affiche le contenu complet d'une page. Il existe
cinq parties pour chaque page.
</para>
<table tocentry="1" id="page-table">
<title>Disposition générale d'une page</title>
<titleabbrev>Disposition d'une page</titleabbrev>
<tgroup cols="2">
<colspec colnum="1" colwidth="0.5*"/>
<colspec colnum="2" colwidth="1.5*"/>
<thead>
<row>
<entry>Élément</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>PageHeaderData</entry>
<entry>Longueur de 24 octets. Contient des informations générales sur la page y compris
des pointeurs sur les espaces libres.</entry>
</row>
<row>
<entry>ItemIdData</entry>
<entry>Tableau de paires (décalage,longueur) pointant sur les éléments réels.
Quatre octets par élément.</entry>
</row>
<row>
<entry>Free space</entry>
<entry>L'espace non alloué. Les pointeurs de nouveaux éléments sont alloués
à partir du début de cette région, les nouveaux éléments à partir de la
fin.</entry>
</row>
<row>
<entry>Items</entry>
<entry>Les éléments eux-mêmes.</entry>
</row>
<row>
<entry>Special space</entry>
<entry>Données spécifiques des méthodes d'accès aux index. Différentes
méthodes stockent différentes données. Vide pour les tables
ordinaires.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Les 24 premiers octets de chaque page consistent en un en-tête de page
(PageHeaderData). Son format est détaillé dans <xref
linkend="pageheaderdata-table"/>. Les deux premiers champs traquent l'entrée
WAL la plus récente relative à cette page. Ensuite se trouve un champ de
deux octets contenant des drapeaux. Ils sont suivis par trois
champs d'entiers sur deux octets (<structfield>pd_lower</structfield>,
<structfield>pd_upper</structfield> et
<structfield>pd_special</structfield>). Ils contiennent des décalages
d'octets à partir du début de la page jusqu'au début de l'espace non alloué,
jusqu'à la fin de l'espace non alloué, et jusqu'au début de l'espace spécial.
Les deux octets suivants de l'en-tête de page,
<structfield>pd_pagesize_version</structfield>, stockent à la fois la taille
de la page et un indicateur de versoin. À partir de la version 8.3 de
<productname>PostgreSQL</productname>, le numéro de version est 4 ;
<productname>PostgreSQL</productname> 8.1 et 8.2 ont utilisé le numéro de version 3 ;
<productname>PostgreSQL</productname> 8.0 a utilisé le numéro de version 2 ;
<productname>PostgreSQL</productname> 7.3 et 7.4 ont utilisé le numéro de
version 1 ; les versions précédentes utilisaient le numéro de version 0.
(La disposition fondamentale de la page et le format de l'en-tête n'ont pas changé
dans la plupart de ces versions mais la disposition de l'en-tête des lignes de tête a
changé.) La taille de la page est seulement présente comme vérification
croisée ; il n'existe pas de support pour avoir plus d'une taille de
page dans une installation.
Le dernier champ est une aide indiquant si traiter la page serait
profitable : il garde l'information sur le plus vieux XMAX non traité
de la page.
</para>
<table tocentry="1" id="pageheaderdata-table">
<title>Disposition de PageHeaderData</title>
<titleabbrev>Disposition de PageHeaderData</titleabbrev>
<tgroup cols="4">
<colspec colnum="1" colwidth="0.8*"/>
<colspec colnum="2" colwidth="0.5*"/>
<colspec colnum="3" colwidth="0.5*"/>
<colspec colnum="4" colwidth="2.2*"/>
<thead>
<row>
<entry>Champ</entry>
<entry>Type</entry>
<entry>Longueur</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>pd_lsn</entry>
<entry>XLogRecPtr</entry>
<entry>8 octets</entry>
<entry>LSN : octet suivant le dernier octet de l'enregistrement
xlog pour la dernière modification de cette page</entry>
</row>
<row>
<entry>pd_tli</entry>
<entry>uint16</entry>
<entry>2 octets</entry>
<entry>TimeLineID de la dernière modification (seulement les 16 bits de
poids faible)</entry>
</row>
<row>
<entry>pd_flags</entry>
<entry>uint16</entry>
<entry>2 octets</entry>
<entry>Bits d'état</entry>
</row>
<row>
<entry>pd_lower</entry>
<entry>LocationIndex</entry>
<entry>2 octets</entry>
<entry>Décalage jusqu'au début de l'espace libre</entry>
</row>
<row>
<entry>pd_upper</entry>
<entry>LocationIndex</entry>
<entry>2 octets</entry>
<entry>Décalage jusqu'à la fin de l'espace libre</entry>
</row>
<row>
<entry>pd_special</entry>
<entry>LocationIndex</entry>
<entry>2 octets</entry>
<entry>Décalage jusqu'au début de l'espace spécial</entry>
</row>
<row>
<entry>pd_pagesize_version</entry>
<entry>uint16</entry>
<entry>2 octets</entry>
<entry>Taille de la page et disposition de l'information du numéro de
version</entry>
</row>
<row>
<entry>pd_prune_xid</entry>
<entry>TransactionId</entry>
<entry>4 bytes</entry>
<entry>Plus vieux XMAX non traité sur la page, ou zéro si aucun</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Tous les détails se trouvent dans
<filename>src/include/storage/bufpage.h</filename>.
</para>
<para>
Après l'en-tête de la page se trouvent les identificateurs d'élément
(<type>ItemIdData</type>), chacun nécessitant quatre octets. Un identificateur
d'élément contient un décalage d'octet vers le début d'un élément, sa
longueur en octets, et quelques bits d'attributs qui affectent son
interprétation. Les nouveaux identificateurs d'éléments sont alloués si
nécessaire à partir du début de l'espace non alloué. Le nombre d'identificateurs
d'éléments présents peut être déterminé en regardant
<structfield>pd_lower</structfield>, qui est augmenté pour allouer un nouvel
identificateur. Comme un identificateur d'élément n'est jamais déplacé tant qu'il
n'est pas libéré, son index pourrait être utilisé sur une base à long terme
pour référencer un élément, même quand l'élément lui-même est déplacé le long de
la page pour compresser l'espace libre. En fait, chaque pointeur vers un
élément (<type>ItemPointer</type>, aussi connu sous le nom de
<type>CTID</type>), créé par <productname>PostgreSQL</productname> consiste
en un numéro de page et l'index de l'identificateur d'élément.
</para>
<para>
Les éléments eux-mêmes sont stockés dans l'espace alloué en marche arrière,
à partir de la fin de l'espace non alloué. La structure exacte varie
suivant le contenu de la table. Les tables et les séquences utilisent toutes
les deux une structure nommée <type>HeapTupleHeaderData</type>, décrite
ci-dessous.
</para>
<para>
La section finale est la <quote>section spéciale</quote> qui pourrait
contenir tout ce que les méthodes d'accès souhaitent stocker. Par exemple,
les index b-tree stockent des liens vers les enfants gauche et droit de la
page ainsi que quelques autres données sur la structure de l'index. Les
tables ordinaires n'utilisent pas du tout de section spéciale (indiquée
en configurant <structfield>pd_special</structfield> à la taille de la page).
</para>
<para>
Toutes les lignes de la table sont structurées de la même façon. Il existe
un en-tête à taille fixe (occupant 23 octets sur la plupart des machines),
suivi par un bitmap NULL optionnel, un champ ID de l'objet optionnel et les
données de l'utilisateur. L'en-tête est détaillé dans <xref
linkend="heaptupleheaderdata-table"/>. Les données réelles de l'utilisateur
(les colonnes de la ligne) commencent àu décalage indiqué par
<structfield>t_hoff</structfield>, qui doit toujours être un multiple de la distance
MAXALIGN pour la plateforme. Le bitmap NULL est seulement présent si le bit
<firstterm>HEAP_HASNULL</firstterm> est initialisé dans
<structfield>t_infomask</structfield>. S'il est présent, il commence juste
après l'en-tête fixe et occupe suffisamment d'octets pour avoir un bit par colonne
de données (c'est-à-dire <structfield>t_natts</structfield> bits ensemble). Dans cette
liste de bits, un bit 1 indique une valeur non NULL, un bit 0 une valeur
NULL. Quand le bitmap n'est pas présent, toutes les colonnes sont supposées
non NULL. L'ID de l'objet est seulement présent si le bit
<firstterm>HEAP_HASOID</firstterm> est initialisé dans
<structfield>t_infomask</structfield>. S'il est présent, il apparaît juste
avant la limite <structfield>t_hoff</structfield>. Tout ajout nécessaire pour faire
de <structfield>t_hoff</structfield> un multiple de MAXALIGN apparaîtra entre le
bitmap NULL et l'ID de l'objet. (Ceci nous assure en retour que l'ID de
l'objet est convenablement aligné.)
</para>
<table tocentry="1" id="heaptupleheaderdata-table">
<title>Disposition de HeapTupleHeaderData</title>
<titleabbrev>Disposition de HeapTupleHeaderData</titleabbrev>
<tgroup cols="4">
<colspec colnum="1" colwidth="0.5*"/>
<colspec colnum="2" colwidth="0.5*"/>
<colspec colnum="3" colwidth="0.5*"/>
<colspec colnum="4" colwidth="2.5*"/>
<thead>
<row>
<entry>Champ</entry>
<entry>Type</entry>
<entry>Longueur</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>t_xmin</entry>
<entry>TransactionId</entry>
<entry>4 octets</entry>
<entry>XID d'insertion</entry>
</row>
<row>
<entry>t_xmax</entry>
<entry>TransactionId</entry>
<entry>4 octets</entry>
<entry>XID de suppression</entry>
</row>
<row>
<entry>t_cid</entry>
<entry>CommandId</entry>
<entry>4 octets</entry>
<entry>CID d'insertion et de suppression (surcharge avec t_xvac)</entry>
</row>
<row>
<entry>t_xvac</entry>
<entry>TransactionId</entry>
<entry>4 octets</entry>
<entry>XID pour l'opération VACUUM déplaçant une version de ligne</entry>
</row>
<row>
<entry>t_ctid</entry>
<entry>ItemPointerData</entry>
<entry>6 octets</entry>
<entry>TID en cours pour cette version de ligne ou pour une version plus
récente</entry>
</row>
<row>
<entry>t_infomask2</entry>
<entry>int16</entry>
<entry>2 octets</entry>
<entry>nombre d'attributs et quelques bits d'état</entry>
</row>
<row>
<entry>t_infomask</entry>
<entry>uint16</entry>
<entry>2 octets</entry>
<entry>différents bits d'options (flag bits)</entry>
</row>
<row>
<entry>t_hoff</entry>
<entry>uint8</entry>
<entry>1 octet</entry>
<entry>décalage vers les données utilisateur</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Tous les détails sont disponibles dans
<filename>src/include/access/htup.h</filename>.
</para>
<para>
Interpréter les données réelles peut seulement se faire avec des informations
obtenues à partir d'autres tables, principalement
<structname>pg_attribute</structname>. Les valeurs clés nécessaires pour
identifier les emplacements des champs sont
<structfield>attlen</structfield> et <structfield>attalign</structfield>.
Il n'existe aucun moyen pour obtenir directement un attribut particulier,
sauf quand il n'y a que des champs de largeur fixe et aucune colonne NULL.
Tout ceci est emballé dans les fonctions
<firstterm>heap_getattr</firstterm>, <firstterm>fastgetattr</firstterm>
et <firstterm>heap_getsysattr</firstterm>.
</para>
<para>
Pour lire les données, vous avez besoin d'examinez chaque attribut à son
tour. Commencez par vérifier si le champ est NULL en fonction du bitmap NULL.
S'il l'est, allez au suivant. Puis, assurez-vous que vous avez le bon
alignement. Si le champ est un champ à taille fixe, alors tous les octets
sont placés simplement. S'il s'agit d'un champ à taille variable
(attlen = -1), alors c'est un peu plus compliqué. Tous les types de données
à longueur variable partagent la même structure commune d'en-tête,
<type>struct varlena</type>, qui inclut la longueur totale de la valeur stockée
et quelques bits d'option. Suivant les options, les données pourraient être
soit dans la table de base soit dans une table <acronym>TOAST</acronym> ;
elles pourraient aussi être compressées (voir <xref
linkend="storage-toast"/>).
</para>
</sect1>
</chapter>