Il y a plusieurs paramètres de configuration associés aux journaux de transaction qui affectent les performances de la base. Cette section explique leur utilisation. Consultez Chapitre 19 pour des détails sur la mise en place de ces paramètres.
Dans la séquence des transactions, les checkpoints (ou points de contrôle) sont des points qui garantissent que les fichiers de données des table et des index ont été mis à jour avec toutes les informations enregistrées dans le journal avant le checkpoint. Au moment du checkpoint, toutes les pages de données modifiées (dirty) sont écrites sur le disque et une entrée spéciale, pour le checkpoint, est écrite dans le journal. (Les modifications étaient déjà envoyées dans les journaux de transactions.) En cas de défaillance, la procédure de récupération recherche le dernier enregistrement d'un checkpoint (enregistrement connus sous le nom de « redo log ») pour déterminer le point des journaux à partir duquel il devra lancer l'opération de REDO. Toute modification effectuée sur les fichiers de données avant ce point a la garantie d'avoir été enregistrée sur disque. Du coup, après un checkpoint, tous les segments représentant des journaux de transaction précédant celui contenant le « redo record » ne sont plus nécessaires et peuvent être soit recyclés, soit supprimés (quand l'archivage des journaux de transaction est activé, ces derniers doivent être archivés avant d'être recyclés ou supprimés).
Un checkpoint doit écrire toutes les pages de données modifiées sur disque, ce qui peut causer une charge disque importante. Pour cette raison, l'activité d'un checkpoint est diluée de façon à ce que les entrées/sorties disque commencent au début du checkpoint et se terminent avant le démarrage du checkpoint suivant ; ceci minimise la dégradation des performances lors des checkpoints.
Le processus checkpointer lance automatiquement un checkpoint
de temps en temps. Il démarre toutes les
checkpoint_timeout secondes
ou si max_wal_size risque d'être dépassé,
suivant ce qui arrive en premier.
La configuration par défaut de ces deux paramètres est,
respectivement, 5 minutes et 1 Go.
Si aucun enregistrement WAL n'a été écrit depuis le dernier checkpoint,
il n'y en aura pas de nouveaux, même si la durée
checkpoint_timeout
est dépassée.
(Si l'archivage des WAL est en place et que vous voulez définir une limite
basse correspondant à la fréquence d'archivage des fichiers de
manière à limiter la perte potentielle de données, vous devez ajuster le
paramètre archive_timeout plutôt que les paramètres
affectant les checkpoints.)
Il est aussi possible de forcer un checkpoint en utilisant la commande SQL
CHECKPOINT
.
La réduction de checkpoint_timeout
et/ou
max_wal_size
provoque
des checkpoints plus fréquents. Cela permet une récupération
plus rapide après arrêt brutal puisqu'il y aura moins d'écritures à
refaire. Cependant, il faut équilibrer cela avec
le coût d'écritures plus fréquentes des pages modifiées.
Si full_page_writes est configuré (ce qui est le
défaut), il reste un autre facteur à considérer. Pour s'assurer de la
cohérence des pages de données, la première modification d'une page de
données après chaque checkpoint résulte dans la journalisation du
contenu de la page en entier. Dans ce cas, un intervalle de checkpoints
plus petit augmentera le volume d'écriture des journaux de transaction,
annulant en partie l'intérêt d'utiliser cet intervalle plus petit et
générant de toute façon plus d'entrées/sorties au niveau disque.
Les checkpoints sont assez coûteux, tout d'abord parce qu'ils
écrivent tous les tampons modifiés jusqu'à ce moment,
et ensuite parce qu'ils génèrent un
trafic supplémentaire dans les journaux de transaction, comme indiqué
ci-dessus. Du coup, il est conseillé
de configurer les paramètres des checkpoints assez haut pour qu'ils
ne surviennent pas trop fréquemment. Pour une vérification
rapide de l'adéquation de vos paramètres, vous pouvez configurer le
paramètre checkpoint_warning.
Si les checkpoints se déclenchent à moins de checkpoint_warning
secondes d'intervalle, un message
est affiché dans les journaux applicatifs du serveur recommandant d'accroître
max_wal_size
. L'apparition occasionnelle d'un tel
message ne doit pas vous alarmer mais, s'il apparaît souvent, alors les
paramètres de contrôle devraient être augmentés. Les opérations en masse,
comme les transferts importants de données via COPY
,
peuvent faire apparaître nombre de ces messages d'avertissement si
vous n'avez pas configuré max_wal_size
avec une valeur
suffisamment haute.
Pour éviter de saturer les entrées/sorties avec de très nombreuses
écritures de pages modifiées, leur écriture pendant le checkpoint
est étalée sur une période de temps. Elle est
contrôlée par checkpoint_completion_target,
donné comme une fraction de l'intervalle des checkpoints.
Le taux d'entrées/sorties est ajusté pour que le checkpoint
se termine quand la fraction indiquée de
checkpoint_timeout
secondes s'est écoulée,
ou avant que max_wal_size
soit dépassé,
selon ce qui arrivera en premier. Avec la valeur par
défaut de 0,5, on peut s'attendre à ce que
PostgreSQL termine chaque checkpoint
dans la moitié du temps avant le démarrage du suivant.
Sur un système
très proche de son débit maximal d'entrées/sorties en fonctionnement
normal, vous pouvez augmenter
checkpoint_completion_target
pour réduire la charge
due aux checkpoints. L'inconvénient
de prolonger les checkpoints est d'impacter le temps de
récupération, car il faudra conserver plus de journaux de transaction
si une récupération est nécessaire. Bien que
checkpoint_completion_target
puisse monter à 1.0, il
le mieux est de la configurer à une valeur plus basse (au
plus 0,9), car les checkpoints incluent d'autres
activités en dehors de l'écriture des pages modifiées. Une valeur de 1,0
peut résulter en checkpoints qui ne se terminent
pas à temps, ce qui entraînerait des baisses de performance à
cause de variations inattendues dans le nombre de journaux nécessaires.
Sur les plateformes Linux et POSIX, checkpoint_flush_after
permet de forcer le système d'exploitation à vider sur disque
les pages écrites par un checkpoint après qu'un nombre configurable
d'octets soit écrit. Sinon ces pages pourraient rester dans
le cache disque du système d'exploitation, provoquant
un blocage quand fsync
est exécuté à la fin d'un
checkpoint. Cette configuration aide souvent à réduire la latence des
transactions mais il peut aussi avoir un effet inverse sur les performances,
particulièrement pour des charges supérieures à
shared_buffers mais
plus petites que le cache disque du système d'exploitation.
Le nombre de fichiers de segments WAL dans le répertoire
pg_wal
dépend des paramètres
min_wal_size
, max_wal_size
et de la
quantité de WAL générée lors des cycles de checkpoints précédents. Quand les
anciens fichiers de segments ne sont plus nécessaires, ils sont supprimés
ou recyclés (c'est-à-dire renommés pour devenir les segments suivants selon
les numéros de la séquence). Si, à cause d'un bref pic du débit
des WAL, max_wal_size
est dépassé, les fichiers inutiles
seront supprimés jusqu'à ce que le système revienne sous cette
limite. En-dessous de cette limite, le système recycle suffisamment de
fichiers WAL pour couvrir le besoin estimé jusqu'au checkpoint suivant, et
supprime le reste. L'estimation est basée sur une moyenne glissante du
nombre de fichiers WAL utilisés dans les cycles de checkpoint précédents.
Elle est augmentée immédiatement si l'utilisation en cours
dépasse l'estimation, pour correspondre aux pics d'utilisation
plutôt qu'à l'utilisation moyenne, jusqu'à un certain point.
min_wal_size
définit un nombre minimum de fichiers
WAL recyclés pour une utilisation future, même si le système est inutilisé
et que l'estimation suggère que peu
de WAL sont nécessaires.
Indépendamment de max_wal_size
, les wal_keep_segments + 1 plus récents fichiers WAL sont conservés en
permanence. De plus, si l'archivage est activé, les anciens segments ne
sont ni supprimés ni recyclés jusqu'à la réussite de leur archivage. Si
l'archivage des WAL n'est pas assez rapide pour tenir le rythme de
la génération des WAL, ou si la commande indiquée par
archive_command
échoue de manière répétée, les anciens
fichiers WAL s'accumuleront dans le répertoire pg_wal
jusqu'à ce que ce problème soit résolu. Un serveur standby lent ou en échec,
et qui utilise un slot de réplication, aura le même effet (voir Section 26.2.6).
En mode de restauration d'archive et en mode standby, le serveur
réalise périodiquement des restartpoints
(points de
redémarrage), qui sont similaire aux checkpoints lors du fonctionnement
normal : le serveur force l'écriture de son état sur disque, met à
jour le fichier pg_control
pour indiquer que les
données déjà traitées des journaux de transactions n'ont plus besoin d'être
parcourues de nouveau, puis recycle les anciens journaux de transactions
trouvés dans le répertoire pg_wal
. Les restartpoints
ne peuvent être réalisés plus fréquemment que les checkpoints du maître car
les restartpoints peuvent seulement être réalisés aux enregistrements de
checkpoint.
Un restartpoint est déclenché lorsqu'un enregistement de checkpoint est
atteint si un minimum de checkpoint_timeout
secondes se
sont écoulées depuis le dernier restartpoint, ou si la taille totale des
journaux de transactions va dépasser max_wal_size
.
Néanmoins, à cause de ces limitations sur quand un restartpoint peut
être effectué, max_wal_size
est souvent dépassé lors
d'une restauration jusqu'à au plus un cycle de checkpoint de journaux
(max_wal_size
n'est de toute façon jamais une limite en
dur, vous devriez donc toujours laisser plein d'espace pour éviter de
manquer d'espace disque).
Il existe deux fonctions WAL internes couramment
utilisées :
XLogInsertRecord
et XLogFlush
.
XLogInsertRecord
est utilisée pour placer une
nouvelle entrée à l'intérieur des tampons WAL en mémoire
partagée. S'il n'y a plus
d'espace pour la nouvelle entrée, XLogInsertRecord
devra écrire (autrement dit, déplacer dans le cache du noyau) quelques
tampons WAL remplis. Ceci n'est pas souhaitable parce que
XLogInsertRecord
est utilisée à chaque
modification bas niveau de la base (par exemple, lors de l'insertion d'une
ligne) quand un verrou exclusif est posé sur les pages de données
affectées, et l'opération doit donc être aussi rapide que possible.
Pire encore, écrire des tampons WAL
peut aussi forcer la création d'un nouveau journal, ce qui
prend encore plus de temps. Normalement, les tampons
WAL doivent être écrits et vidés par un appel
à XLogFlush
fait, la plupart du
temps, au moment de la validation d'une transaction pour assurer
que les entrées de la transaction sont écrites vers un stockage
permanent. Sur les systèmes avec une importante écriture de journaux,
les requêtes de XLogFlush
peuvent ne pas
arriver assez souvent pour empêcher XLogInsert
d'avoir
à écrire lui-même. Sur de tels systèmes, on devrait augmenter le
nombre de tampons
WAL en modifiant le paramètre wal_buffers. Quand full_page_writes est configuré
et que le système est très occupé, configurer wal_buffers
avec une valeur
plus importante aide à lisser les temps de réponse
dans la période suivant immédiatement chaque checkpoint.
Le paramètre commit_delay définit combien de
micro-secondes un processus maître d'un groupe de commit
va s'endormir après avoir obtenu un verrou avec XLogFlush
,
pendant que les autres processus du groupe vont s'ajouter à la queue
derrière le maître. Ce délai permet aux autres processus serveur
d'ajouter leurs enregistrements de commit aux buffers WAL, pour
qu'ils soient tous écrits par un éventuel vidage sur disque du
maître. Il n'y aura pas d'endormissement si fsync n'est
pas activé, ou si moins de commit_siblings autres
sessions sont actuellement dans une transaction active ; ce
mécanisme évite l'endormissement quand il est improbable que d'autres sessions
valident bientôt leur transactions. Il est à noter que, sur
certaines plateformes, la résolution d'une requête d'endormissement est de dix
millisecondes, ce qui implique que toute valeur comprise entre 1 et 10000 pour
le paramètre commit_delay
aura le même effet. Notez aussi
que, sur certaines plateformes, les opérations d'endormissement
peuvent être légèrement plus longues que ce qui a été demandé par le paramètre.
Comme l'objet de commit_delay
est de permettre d'amortir
le coût de chaque opération de vidage sur disque sur plusieurs transactions concurrentes
(potentiellement au prix de la latence des transactions), il est nécessaire de
quantifier ce coût pour choisir intelligemment la valeur de ce paramètre. Plus le
coût est élevé, plus commit_delay
sera efficace
au sein d'un débit de transactions croissant,
jusqu'à un certain point. Le programme pg_test_fsync peut être
utilisé pour mesurer le temps moyen en microsecondes que prend une seule
opération de vidage de WAL.
La moitié du temps moyen rapporté par ce
programme pour une mise à jour d'une simple opération d'écriture de 8 ko
est la valeur la plus souvent recommandée comme point de départ de
l'optimisation d'une
charge particulière. Bien que l'ajustement de la valeur de
commit_delay
soit particulièrement utile lorsque les
journaux WAL sont stockés sur des disques à latence élevée, le gain peut
aussi être significatif sur les supports de stockage avec des temps de
synchronisation très rapides, comme les SSD ou les grappes RAID
avec des caches en écriture dotés de batterie ; mais dans tous les cas,
cela doit être testé avec une charge représentative de la réalité. Des
valeurs plus élevées de commit_siblings
peuvent être
utilisées dans ce cas, alors que de petites valeurs de
commit_siblings
sont souvent utiles sur des supports de
grande latence. À noter qu'il est possible qu'une valeur trop élevée de
commit_delay
augmente la latence des transactions
à un tel point que le débit des transactions en souffre.
Lorsque commit_delay
est défini à zéro (il s'agit de la
valeur par défaut), il est toujours possible qu'un regroupement de commits se
produise, mais chaque groupe ne consistera qu'en sessions atteignant
le moment de l'enregistrement de commit pendant le laps de temps
où la précédente opération de vidage (s'il y en a) opère.
Avec un grand nombre de clients, un « effet tunnel »
(gangway effect) a tendance à se produire,
et ainsi les effets du regroupement de commits deviennent significatifs même
lorsque commit_delay
est à zéro, et dans ce cas
commit_delay
devient inutile. Définir
commit_delay
n'est utile que quand
(1) il existe des transactions concurrentes, et
(2) le débit est limité dans une certaine mesure
par la vitesse de commit ; mais, dans le cas d'un temps de latence
du disque élevé, ce paramètre peut augmenter efficacement le flux de transaction
avec seulement deux clients
(c'est-à-dire un unique client qui valide, et une transaction sœur).
Le paramètre wal_sync_method détermine comment
PostgreSQL demande au noyau de forcer les mises
à jour des journaux de transaction sur le disque. Toutes les différentes
options devraient être identiques en terme de fiabilité, à l'exception de
fsync_writethrough
, qui peut parfois forcer une écriture
du cache disque même quand d'autres options ne le font pas. Néanmoins,
connaître l'option la plus rapide est assez dépendant de la plateforme.
Vous pouvez tester les vitesses des différentes
options en utilisant le programme pg_test_fsync. Notez que ce paramètre est ignoré si
fsync
a été désactivé.
Activer le paramètre de configuration wal_debug (à
supposer que PostgreSQL ait été compilé avec le
support de ce paramètre) permet d'enregistrer chaque appel
WAL à XLogInsertRecord
et
XLogFlush
dans les journaux applicatifs du serveur.
Cette option
pourrait être remplacée par un mécanisme plus général dans le futur.