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 20 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(configuré en utilisant
checkpoint_timeout
). 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,9, on peut s'attendre à ce que
PostgreSQL termine chaque checkpoint un peu
avant le prochain checkpoint planifié (à environ 90% de la durée du
dernier checkpoint). Ceci étant les I/O autant que possible pour que la
charge en I/O des checkpoints soit lissée pendant tout l'intervalle des
checkpoints. L'inconvénient est que prolonger les checkpoints affecte le
temps de redémarrage après crash parce qu'un plus grand nombre de segments
WAL devront être conservés pour une possible utilisation après crash. Un
utilisateur inquiet par la durée requise pour la restauration après crash
pourrait vouloir réduire checkpoint_timeout
pour que
les checkpoints surviennent plus fréquemment, tout en diluant les I/O sur
l'intervalle des checkpoints. Alternativement,
checkpoint_completion_target
pourrait être réduit mais
cela résulterait en des moments avec des I/O plus intenses (lors du
checkpoint) et des momentsavec des I/O moins intenses (après la fin du
checkpoint mais avant le début du suivant). De ce fait, cela n'est pas
recommandé. Bien que checkpoint_completion_target
puisse être configuré aussi haut que 1,0, il est typiquement recommandé de
le configurer pas plus haut que 0,9 (la valeur par défaut) car les
checkpoints ont quelques autres activités en dehors de l'écriture du cache
modifié. 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_size méga-octets les plus récents des fichiers
WAL et un fichier WAL supplémentaire 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 archive_command
ou
archive_library
é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 27.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 primaire 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.
Il existe deux fonctions internes pour écrire des données WAL sur
disque : XLogWrite
et
issue_xlog_fsync
. Quand track_wal_io_timing est activé, le nombre total de fois où
XLogWrite
écrit et
issue_xlog_fsync
synchronise des données WAL sur
disque est compté comme, respectivement, wal_write_time
et wal_sync_time
dans pg_stat_wal. XLogWrite
est
normalement appelé par XLogInsertRecord
(quand il n'y
a plus d'espace pour le nouvel enregistrement dans le cachedes WAL),
XLogFlush
et le walwriter, pour écrire le cache WAL
sur disque et appeler issue_xlog_fsync
.
issue_xlog_fsync
est normalement appelé par
XLogWrite
pour synchroniser les fichiers WAL sur
disque. Si wal_sync_method
vaut soit
open_datasync
soit open_sync
, une
opération d'écriture dans XLogWrite
garantit de
synchroniser les données des WAL écrites sur disque et
issue_xlog_fsync
ne fait rien. Si
wal_sync_method
vaut soit fdatasync
,
soit fsync
, soit fsync_writethrough
,
les opérations en écriture déplacent le cache WAL dans le cache du noyau
et issue_xlog_fsync
les synchronise sur disque.
Quelque soit la configuration de track_wal_io_timing
,
le nombre de fois où XLogWrite
écrit et que
issue_xlog_fsync
synchronise les données WAL sur
disque sont aussi comptées dans, respectivement,
wal_write
et wal_sync
de la vue
pg_stat_wal
.
Le paramètre recovery_prefetch peut être utilisé pour
réduite les durées d'attente en entrée/sortie pendant une restauration en
indiquant au niveau d'initier des lecteurs de blocs disques qui seront
bientôt nécessaires mais qui ne sont pas actuellement dans le cache disque de
PostgreSQL. Les paramètres maintenance_io_concurrency et wal_decode_buffer_size limitent, respectivement, la
concurrence et la distance sur les lectures en avance. Par défaut, c'est
configuré à try
, ce qui active la fonctionnalité sur les
systèmes où posix_fadvise
est disponible.