52.3. Emplacement des pages de la base de données
Cette section fournit un aperçu du format des pages utilisées par les
tables et index de PostgreSQL™.[] Les
séquences et les tables TOAST
tables sont formatées comme des tables standards.
Dans l'explication qui suit, un octet
contient huit bits. De plus, le terme élément 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.
Chaque table et index est stocké comme un tableau de pages 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 métapage 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.
Tableau 52.2,
« Disposition d'une page » affiche le contenu complet
d'une page. Il existe cinq parties pour chaque page.
Tableau 52.2. Disposition générale d'une page
|
Élément
|
Description
|
|
PageHeaderData
|
Longueur de 20 octets. Contient des informations générales
sur la page y compris des pointeurs sur les espaces libres.
|
|
ItemPointerData
|
Tableau de paires (décalage,longueur) pointant sur les
éléments réels. Quatre octets par élément.
|
|
Free space
|
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.
|
|
Items
|
Les éléments eux-mêmes.
|
|
Special space
|
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.
|
Les vingt premiers octets de chaque page consistent en un en-tête de
page (PageHeaderData). Son format est détaillé dans Tableau 52.3,
« Disposition de PageHeaderData ». Les deux premiers
champs traquent l'entrée WAL la plus récente relative à cette page.
Ils sont suivis par trois champs d'entiers sur deux octets
(
pd_lower
,
pd_upper
et
pd_special
). 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 derniers octets de
l'en-tête de page,
pd_pagesize_version
, stockent à la fois
la taille de la page et un indicateur de versoin. À partir de la
version 8.1 de PostgreSQL™, le
numéro de version est 3 ; PostgreSQL™ 8.0 a utilisé le numéro de
version 2 ; PostgreSQL™ 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 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.
Tableau 52.3. Disposition de PageHeaderData
|
Champ
|
Type
|
Longueur
|
Description
|
|
pd_lsn
|
XLogRecPtr
|
8 octets
|
LSN : octet suivant le dernier octet de l'enregistrement
xlog pour la dernière modification de cette page
|
|
pd_tli
|
TimeLineID
|
4 octets
|
TLI de la dernière modification
|
|
pd_lower
|
LocationIndex
|
2 octets
|
Décalage jusqu'au début de l'espace libre
|
|
pd_upper
|
LocationIndex
|
2 octets
|
Décalage jusqu'à la fin de l'espace libre
|
|
pd_special
|
LocationIndex
|
2 octets
|
Décalage jusqu'au début de l'espace spécial
|
|
pd_pagesize_version
|
uint16
|
2 octets
|
Taille de la page et disposition de l'information du numéro
de version
|
Tous les détails se trouvent dans src/include/storage/bufpage.h.
Après l'en-tête de la page se trouvent les identificateurs d'élément
(ItemIdData), 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
pd_lower
, 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
(ItemPointer, aussi connu sous le nom de
CTID), créé par PostgreSQL™ consiste en un numéro de page
et l'index de l'identificateur d'élément.
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 HeapTupleHeaderData, décrite ci-dessous.
La section finale est la « section
spéciale » 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
pd_special
à la
taille de la page).
Toutes les lignes de la table sont structurées de la même façon. Il
existe un en-tête à taille fixe (occupant 27 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 Tableau 52.4,
« Disposition de HeapTupleHeaderData ». Les données
réelles de l'utilisateur (les colonnes de la ligne) commencent àu
décalage indiqué par
t_hoff
,
qui doit toujours être un multiple de la distance MAXALIGN pour la
plateforme. Le bitmap NULL est seulement présent si le bit HEAP_HASNULL est initialisé dans
t_infomask
. 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
t_natts
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
HEAP_HASOID est initialisé dans
t_infomask
. S'il est présent, il apparaît
juste avant la limite
t_hoff
.
Tout ajout nécessaire pour faire de
t_hoff
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é.)
Tableau 52.4. Disposition de HeapTupleHeaderData
|
Champ
|
Type
|
Longueur
|
Description
|
|
t_xmin
|
TransactionId
|
4 octets
|
insère le tampon XID
|
|
t_cmin
|
CommandId
|
4 octets
|
insère le tampon CID
|
|
t_xmax
|
TransactionId
|
4 octets
|
supprime le tampon XID
|
|
t_cmax
|
CommandId
|
4 octets
|
supprime le tampon CID (surcharge avec t_xvac)
|
|
t_xvac
|
TransactionId
|
4 octets
|
XID pour l'opération VACUUM déplaçant une version de ligne
|
|
t_ctid
|
ItemPointerData
|
6 octets
|
TID en cours pour cette version de ligne ou pour une
version plus récente
|
|
t_natts
|
int16
|
2 octets
|
nombre d'attributs
|
|
t_infomask
|
uint16
|
2 octets
|
différents bits d'options (flag bits)
|
|
t_hoff
|
uint8
|
1 octet
|
décalage vers les données utilisateur
|
Tous les détails sont disponibles dans src/include/access/htup.h.
Interpréter les données réelles peut seulement se faire avec des
informations obtenues à partir d'autres tables, principalement
pg_attribute. Les valeurs clés
nécessaires pour identifier les emplacements des champs sont
attlen
et
attalign
. 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 heap_getattr,
fastgetattr et heap_getsysattr.
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, varattrib, 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 TOAST ; elles pourraient aussi être compressées
(voir Section 52.2, « TOAST »).