WAL dans PostgreSQL: 4. Configuration du journal

Nous avons donc pris connaissance du périphérique du cache tampon et, à l'aide de son exemple, nous avons réalisé que lorsque le contenu de la RAM disparaissait en cas de panne, un journal de pré-enregistrement est nécessaire pour la récupération. La taille des fichiers journaux requis et le temps de récupération sont limités en raison d'un point de contrôle exécuté périodiquement.

Dans les articles précédents, nous avons déjà examiné un assez grand nombre de paramètres importants, d'une manière ou d'une autre liés à la revue. Dans cet article (le dernier de cette série), nous examinerons les problèmes de réglage qui n'ont pas encore été abordés: les niveaux de journalisation et leur objectif, ainsi que la fiabilité et les performances de la journalisation.

Niveaux de journalisation


Le principal objectif du journal de pré-enregistrement est de fournir la possibilité de récupérer après une défaillance. Mais, si vous devez encore tenir un journal, il peut être adapté à d'autres tâches, en y ajoutant une certaine quantité d'informations supplémentaires. Il existe plusieurs niveaux de journalisation. Ils sont définis par le paramètre wal_level et sont organisés de sorte que le journal de chaque niveau suivant comprenne tout ce qui tombe dans le journal du niveau précédent, plus quelque chose de nouveau.

Minimal


Le niveau minimum possible est défini par la valeur wal_level = minimal et garantit uniquement la récupération après une panne. Pour économiser de l'espace, les opérations liées au traitement de masse des données (telles que CREATE TABLE AS SELECT ou CREATE INDEX) ne sont pas enregistrées. Au lieu de cela, les données nécessaires sont immédiatement écrites sur le disque et un nouvel objet est ajouté au répertoire système et devient visible lorsque la transaction est validée. Si une défaillance se produit pendant l'opération, les données déjà enregistrées restent invisibles et ne violent pas la cohérence. Si l'échec se produit une fois l'opération terminée, tout le nécessaire est déjà arrivé sur le disque et n'a pas besoin d'être enregistré.

Voyons voir. Tout d'abord, définissez le niveau requis (pour cela, vous devrez également modifier un autre paramètre - max_wal_senders ).

=> ALTER SYSTEM SET wal_level = minimal; => ALTER SYSTEM SET max_wal_senders = 0; 

 student$ sudo pg_ctlcluster 11 main restart 

Notez que la modification du niveau nécessite un redémarrage du serveur.

Rappelez-vous la position actuelle dans le journal:

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353927BC (1 row) 

Créons maintenant la table (CREATE TABLE AS SELECT) et réécrivons la position dans le journal. La quantité de données sélectionnées par l'instruction SELECT n'a pas d'importance dans ce cas, nous nous limiterons donc à une seule ligne.

 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353A7DFC (1 row) 

Avec l'utilitaire pg_waldump familier, regardons les entrées du journal.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353927BC -e 0/353A7DFC 

Bien sûr, certains détails peuvent différer d'un lancement à l'autre, mais dans ce cas, c'est ce qui s'est produit. L'entrée du gestionnaire Heap2 fait référence au nettoyage, il s'agit ici d'un nettoyage en ligne de l'une des tables du catalogue système (les objets système se distinguent facilement à l'œil nu par le nombre «court» en rel):

 rmgr: Heap2 len (rec/tot): 59/ 7587, tx: 0, lsn: 0/353927BC, prev 0/35392788, desc: CLEAN remxid 101126, blkref #0: rel 1663/16386/1247 blk 8 FPW 

Ensuite, il y a un enregistrement sur l'obtention du prochain OID pour la table que nous allons créer:

 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/35394574, prev 0/353927BC, desc: NEXTOID 82295 

Maintenant, la création réelle de la table:

 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/35394594, prev 0/35394574, desc: CREATE base/16386/74103 

Cependant, l'insertion de données dans une table n'est pas enregistrée. Ensuite, il existe de nombreuses entrées sur l'insertion de lignes dans différentes tables et index - ce PostgreSQL enregistre la table créée dans le répertoire système (je la donne sous forme abrégée):

 rmgr: Heap len (rec/tot): 203/ 203, tx: 101127, lsn: 0/353945C0, prev 0/35394594, desc: INSERT off 71, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 685, tx: 101127, lsn: 0/3539468C, prev 0/353945C0, desc: INSERT_LEAF off 37, blkref #0: rel 1663/16386/2703 blk 2 FPW ... rmgr: Btree len (rec/tot): 53/ 2393, tx: 101127, lsn: 0/353A747C, prev 0/353A6788, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

Et enfin, la fixation des transactions:

 rmgr: Transaction len (rec/tot): 34/ 34, tx: 101127, lsn: 0/353A7DD8, prev 0/353A747C, desc: COMMIT 2019-07-23 18:59:34.923124 MSK 

Réplique


Lorsque nous restaurons le système à partir de la sauvegarde, nous partons d'un état du système de fichiers et amenons progressivement les données au point de récupération, en lisant les entrées de journal archivées. Le nombre de ces enregistrements peut être très important (par exemple, plusieurs jours), c'est-à-dire que la période de récupération couvrira non pas un point de contrôle, mais plusieurs. Par conséquent, il est clair que le niveau minimum du journal n'est pas suffisant - si une opération n'est pas enregistrée, nous ne saurons tout simplement pas qu'elle doit être répétée. Pour restaurer à partir d'une sauvegarde, toutes les opérations doivent être enregistrées.

Il en va de même pour la réplication - tout ce qui n'est pas enregistré ne sera pas transféré vers la réplique et ne sera pas reproduit. Mais, si nous voulons exécuter des requêtes sur une réplique, c'est toujours compliqué.

Tout d'abord, nous avons besoin d'informations sur les verrous exclusifs qui se produisent sur le serveur principal, car ils peuvent entrer en conflit avec les demandes sur la réplique. Ces verrous sont enregistrés et appliqués sur la réplique (au nom du processus de démarrage).

Deuxièmement, vous devez pouvoir créer des instantanés de données , et pour cela, comme nous nous en souvenons, des informations sur les transactions en cours sont nécessaires. Dans le cas d'une réplique, nous parlons non seulement de transactions locales, mais également de transactions sur le serveur principal. La seule façon de transmettre ces informations est de les écrire périodiquement dans le journal (cela se produit toutes les 15 secondes).

Le niveau de journalisation, qui garantit à la fois la possibilité de récupérer à partir d'une sauvegarde et la possibilité de réplication physique, est défini par la valeur wal_level = replica . (Avant la version 9.6, il y avait deux niveaux distincts d'archive et hot_standby, mais ensuite ils étaient combinés en un seul commun.)

À partir de PostgreSQL 10, c'est ce niveau qui est défini par défaut (et avant cela, il était minimal). Par conséquent, il suffit de réinitialiser les paramètres aux valeurs par défaut:

 => ALTER SYSTEM RESET wal_level; => ALTER SYSTEM RESET max_wal_senders; 

 student$ sudo pg_ctlcluster 11 main restart 

Nous supprimons la table et répétons exactement la même séquence d'actions que la dernière fois:

 => DROP TABLE wallevel; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353AF21C (1 row) 
 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353BE51C (1 row) 

Vérifiez maintenant les entrées de journal.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353AF21C -e 0/353BE51C 

Nettoyage, obtention de l'OID, création d'une table et enregistrement dans le répertoire système - pour l'instant, tout est comme ça:

 rmgr: Heap2 len (rec/tot): 58/ 58, tx: 0, lsn: 0/353AF21C, prev 0/353AF044, desc: CLEAN remxid 101128, blkref #0: rel 1663/16386/1247 blk 8 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/353AF258, prev 0/353AF21C, desc: NEXTOID 82298 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/353AF278, prev 0/353AF258, desc: CREATE base/16386/74106 rmgr: Heap len (rec/tot): 203/ 203, tx: 101129, lsn: 0/353AF2A4, prev 0/353AF278, desc: INSERT off 73, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 717, tx: 101129, lsn: 0/353AF370, prev 0/353AF2A4, … rmgr: Btree len (rec/tot): 53/ 2413, tx: 101129, lsn: 0/353BD954, prev 0/353BCC44, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

Mais quelque chose de nouveau. L'enregistrement d'un verrou exclusif lié au gestionnaire de secours - dans ce cas, il bloque le numéro de transaction (pourquoi est-il nécessaire, nous parlerons en détail dans la prochaine série d'articles):

 rmgr: Standby len (rec/tot): 42/ 42, tx: 101129, lsn: 0/353BE2D8, prev 0/353BD954, desc: LOCK xid 101129 db 16386 rel 74106 

Et ceci est un enregistrement sur l'insertion de lignes dans notre table (comparez le numéro de fichier rel avec celui indiqué ci-dessus dans l'enregistrement CREATE):

 rmgr: Heap len (rec/tot): 59/ 59, tx: 101129, lsn: 0/353BE304, prev 0/353BE2D8, desc: INSERT+INIT off 1, blkref #0: rel 1663/16386/74106 blk 0 

Enregistrement de validation:

 rmgr: Transaction len (rec/tot): 421/ 421, tx: 101129, lsn: 0/353BE340, prev 0/353BE304, desc: COMMIT 2019-07-23 18:59:37.870333 MSK; inval msgs: catcache 74 catcache 73 catcache 74 catcache 73 catcache 50 catcache 49 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 snapshot 2608 relcache 74106 snapshot 1214 

Et un autre enregistrement, qui se produit périodiquement et n'est pas lié à la transaction terminée, fait référence au gestionnaire de secours et rend compte des transactions en cours:

 rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 0/353BE4E8, prev 0/353BE340, desc: RUNNING_XACTS nextXid 101130 latestCompletedXid 101129 oldestRunningXid 101130 

Logique


Enfin, le dernier niveau est fixé par la valeur du paramètre wal_level = logique et offre la possibilité de décodage logique et de réplication logique. Il doit être activé sur le serveur de publication.

Du point de vue des entrées de journal, ce niveau n'est pratiquement pas différent des répliques - les enregistrements liés aux origines de réplication et les entrées logiques arbitraires qui peuvent être ajoutées au journal des applications sont ajoutés. Fondamentalement, le décodage logique dépend des informations sur les transactions en cours, car vous devez créer un instantané des données pour suivre les modifications dans le catalogue système.

Maintenant, nous n'entrerons pas dans les détails du fonctionnement de la sauvegarde et de la réplication - c'est un gros sujet pour une série d'articles séparée.

Fiabilité record


Il est clair que le mécanisme de journalisation doit être fiable et fournir des garanties de possibilité de récupération dans toutes les situations (sans rapport, bien sûr, avec des dommages au support de données). La fiabilité est influencée par de nombreux facteurs, dont nous prendrons en compte la mise en cache, la corruption des données et l'atomicité des enregistrements.

Mise en cache


Il existe de nombreux caches sur le chemin de données vers un stockage non volatile (tel qu'un disque dur).

Lorsqu'un programme (n'importe lequel, mais dans notre cas PostgreSQL) demande au système d'exploitation d'écrire quelque chose sur le disque, le système d'exploitation transfère les données vers son cache en RAM. L'enregistrement réel se produit de manière asynchrone, en fonction des paramètres du planificateur d'E / S du système d'exploitation.

Lorsque le système d'exploitation décide d'écrire des données, elles tombent dans le cache du lecteur (disque dur). L'électronique du lecteur peut également retarder l'enregistrement, par exemple, la collecte de données dans des groupes qui sont plus rentables à enregistrer en même temps. Et si un contrôleur RAID est utilisé, un autre niveau de mise en cache apparaît entre le système d'exploitation et le lecteur.

Ainsi, si vous ne prenez pas de mesures spéciales, il est difficile de savoir quand les données seront réellement stockées en toute sécurité. Ce n'est généralement pas important, mais il existe des endroits critiques où PostgreSQL doit être sûr que les données sont écrites en toute sécurité. Tout d'abord, il s'agit de la journalisation (si l'entrée de journal n'a pas atteint le disque, elle disparaîtra avec le reste du contenu de la RAM) et un point de contrôle (il faut être sûr que les pages sales sont bien écrites sur le disque). Mais il y a d'autres situations, par exemple, l'exécution d'opérations non journalisées au niveau minimum, etc.

Le système d'exploitation fournit des outils qui doivent garantir l'écriture immédiate des données dans une mémoire non volatile. Il existe plusieurs options, mais elles se résument à deux principales: soit une commande de synchronisation est donnée après l'enregistrement (fsync, fdatasync), soit lors de l'ouverture d'un fichier (ou de l'écriture), un indicateur spécial est indiqué pour la synchronisation ou même l'enregistrement direct, en contournant le cache du système d'exploitation.

En ce qui concerne le journal, l'utilitaire pg_test_fsync vous permet de choisir la méthode la plus appropriée pour un système d'exploitation et un système de fichiers spécifiques, et il est installé dans le paramètre de configuration wal_sync_method . Les fichiers normaux sont toujours synchronisés à l'aide de fsync.

Le point subtil est que lors du choix d'une méthode, les caractéristiques de l'équipement doivent être prises en compte. Par exemple, si vous utilisez un contrôleur pris en charge par une batterie de secours, il n'y a aucune raison de ne pas utiliser son cache, car la batterie enregistre les données en cas de panne de courant.

La documentation contient de nombreux détails à ce sujet.

Dans tous les cas, la synchronisation est coûteuse et n'a pas lieu plus souvent qu'absolument nécessaire (nous reviendrons sur ce problème un peu plus bas lorsque nous parlerons de performances).

De manière générale, la synchronisation peut être désactivée (le paramètre fsync en est responsable), mais dans ce cas, vous devez oublier la fiabilité du stockage. En désactivant fsync , vous acceptez que les données puissent être irrémédiablement perdues à tout moment. La seule option raisonnable pour utiliser cette option est probablement d'augmenter temporairement la productivité, lorsque les données peuvent être facilement restaurées à partir d'une autre source (par exemple, lors de la migration initiale).

Corruption des données


L'équipement est imparfait et les données peuvent être endommagées sur le support lors de la transmission de données via des câbles d'interface, etc. Certaines de ces erreurs sont traitées au niveau matériel, mais d'autres ne le sont pas.

Afin de détecter le problème à temps, les écritures de journal sont toujours fournies avec des sommes de contrôle.

Les pages de données peuvent également être protégées par des sommes de contrôle. Pour l'instant, cela ne peut être fait que lorsque le cluster est initialisé, mais dans PostgreSQL 12, il sera possible de les activer et de les désactiver à l'aide de l'utilitaire pg_checksums (mais pas encore à la volée, mais uniquement lorsque le serveur est arrêté).

Dans un environnement de production, des sommes de contrôle doivent être incluses, malgré les frais généraux de leur calcul et de leur contrôle. Cela réduit la probabilité qu'une défaillance ne soit pas détectée à temps.

Réduit, mais n'élimine pas.
Tout d'abord, les sommes de contrôle ne sont vérifiées que lors de l'accès à la page - par conséquent, les dommages peuvent passer inaperçus jusqu'à ce qu'ils atteignent toutes les sauvegardes. C'est pourquoi pg_probackup vérifie les sommes de contrôle de toutes les pages du cluster pendant la sauvegarde.
Deuxièmement, une page remplie de zéros est considérée comme correcte - si le système de fichiers "annule" par erreur le fichier, cela peut passer inaperçu.
Troisièmement, les sommes de contrôle ne protègent que la couche principale des fichiers de données. Les couches restantes et les autres fichiers (par exemple, les états des transactions XACT) ne sont protégés par rien.
Hélas.

Voyons comment cela fonctionne. Tout d'abord, assurez-vous que les sommes de contrôle sont activées (gardez à l'esprit que ce n'est pas le cas lors de l'installation d'un paquet sur des systèmes de type Debian):

 => SHOW data_checksums; 
  data_checksums ---------------- on (1 row) 

Le paramètre data_checksums est en lecture seule.

Voici le fichier dans lequel se trouve notre table:

 => SELECT pg_relation_filepath('wallevel'); 
  pg_relation_filepath ---------------------- base/16386/24890 (1 row) 

Arrêtez le serveur et modifiez quelques octets dans la page zéro, par exemple, supprimez la dernière entrée de journal de l'en-tête LSN.

 student$ sudo pg_ctlcluster 11 main stop 

 postgres$ dd if=/dev/zero of=/var/lib/postgresql/11/main/base/16386/24890 oflag=dsync conv=notrunc bs=1 count=8 
 8+0 records in 8+0 records out 8 bytes copied, 0,0083022 s, 1,0 kB/s 

En principe, le serveur n'a pas pu être arrêté. Il suffit que la page ait été écrite sur le disque et expulsée du cache (sinon le serveur fonctionnera avec la page du cache). Mais un tel scénario est plus difficile à reproduire.

Maintenant, nous démarrons le serveur et essayons de lire le tableau.

 student$ sudo pg_ctlcluster 11 main start 

 => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 ERROR: invalid page in block 0 of relation base/16386/24890 

Mais que faire si les données ne peuvent pas être restaurées à partir de la sauvegarde? Le paramètre ignore_checksum_failure vous permet d'essayer de lire le tableau, naturellement avec le risque d'obtenir des données déformées.

 => SET ignore_checksum_failure = on; => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 n --- 1 (1 row) 

Bien sûr, dans ce cas, tout se passe bien, car nous avons foiré uniquement le titre de la page, et non les données elles-mêmes.

Et encore une chose. Lorsque les sommes de contrôle sont activées, des bits d'invites sont écrits dans le journal (nous les avons examinés plus tôt), car une modification de n'importe quel bit, même non essentiel, entraîne également une modification de la somme de contrôle. Lorsque les sommes de contrôle sont désactivées, le paramètre wal_log_hints est responsable de l'écriture des bits de conseil dans le journal .

Les modifications apportées aux bits de conseil sont toujours enregistrées sous la forme d'une image pleine page (FPI, image pleine page), ce qui augmente la taille du journal dans l'ordre. Dans ce cas, il est logique d'activer la compression d'images complètes à l'aide du paramètre wal_compression (ce paramètre est apparu dans la version 9.5). Ci-dessous, nous examinons des chiffres spécifiques.

Record d'atomicité


Et enfin, il y a le problème de l'atomicité du disque. Une page de base de données prend au moins 8 Ko (elle peut être de 16 ou 32 Ko) et, à un faible niveau, l'enregistrement se produit dans des blocs généralement plus petits (généralement 512 octets ou 4 Ko). Par conséquent, en cas de panne de courant, la page de données peut être partiellement enregistrée. Il est clair que pendant la récupération, cela n'a aucun sens d'appliquer des entrées de journal ordinaires à une telle page.

Pour la protection, PostgreSQL vous permet d'écrire dans le journal l' image complète de la page lors de sa première modification après le début du point de contrôle (la même image est enregistrée lorsque les bits de l'info-bulle changent). Le paramètre full_page_writes contrôle cela et il est activé par défaut.

Si une image de page est rencontrée lors de la restauration dans un journal, elle est inconditionnellement (sans vérification LSN) écrite sur le disque: il y a plus de confiance en elle, car, comme tout enregistrement de journal, elle est protégée par une somme de contrôle. Et des entrées de journal déjà régulières sont appliquées à cette image correcte garantie.

Bien que PostgreSQL exclue l'espace non alloué de l'image pleine page (nous avons précédemment examiné la structure des blocs), le volume des entrées de journal générées augmente considérablement. Comme déjà mentionné, la situation peut être améliorée en compressant les images complètes (paramètre wal_compression ).

Afin de ressentir en quelque sorte le changement de la taille du journal, nous allons mener une expérience simple en utilisant l'utilitaire pgbench. Initialisons:

 student$ pgbench -i test 
 dropping old tables... creating tables... generating data... 100000 of 100000 tuples (100%) done (elapsed 0.15 s, remaining 0.00 s) vacuuming... creating primary keys... done. 

L' option full_page_writes est activée:

 => SHOW full_page_writes; 
  full_page_writes ------------------ on (1 row) 

Exécutez le point d'arrêt et exécutez immédiatement le test pendant 30 secondes.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/38E04A08 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26851 latency average = 1.117 ms tps = 895.006720 (including connections establishing) tps = 895.095229 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C478 (1 row) 

Taille du journal:

 => SELECT pg_size_pretty('0/3A69C478'::pg_lsn - '0/38E04A08'::pg_lsn); 
  pg_size_pretty ---------------- 25 MB (1 row) 

Désactivez maintenant le paramètre full_page_writes:

 => ALTER SYSTEM SET full_page_writes = off; => SELECT pg_reload_conf(); 

Et répétez l'expérience.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C530 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 27234 latency average = 1.102 ms tps = 907.783080 (including connections establishing) tps = 907.895326 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87658 (1 row) 

Taille du journal:

 => SELECT pg_size_pretty('0/3BE87658'::pg_lsn - '0/3A69C530'::pg_lsn); 
  pg_size_pretty ---------------- 24 MB (1 row) 

Oui, la taille a diminué, mais pas du tout aussi importante qu'on pourrait s'y attendre.

La raison en est que le cluster est initialisé avec des sommes de contrôle dans les pages de données, et que vous devez donc toujours écrire des images pleine page dans le journal lorsque vous modifiez les bits de l'info-bulle. Ces données (dans notre cas) représentent environ la moitié du volume total, ce qui peut être vu en regardant les statistiques:

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump --stats -p /var/lib/postgresql/11/main/pg_wal -s 0/3A69C530 -e 0/3BE87658 
 Type N (%) Record size (%) FPI size (%) ---- - --- ----------- --- -------- --- XLOG 1721 ( 1,03) 84329 ( 0,77) 13916104 (100,00) Transaction 27235 ( 16,32) 926070 ( 8,46) 0 ( 0,00) Storage 1 ( 0,00) 42 ( 0,00) 0 ( 0,00) CLOG 1 ( 0,00) 30 ( 0,00) 0 ( 0,00) Standby 4 ( 0,00) 240 ( 0,00) 0 ( 0,00) Heap2 27522 ( 16,49) 1726352 ( 15,76) 0 ( 0,00) Heap 109691 ( 65,71) 8169121 ( 74,59) 0 ( 0,00) Btree 756 ( 0,45) 45380 ( 0,41) 0 ( 0,00) -------- -------- -------- Total 166931 10951564 [44,04%] 13916104 [55,96%] 

Pour des raisons de compacité, j'ai supprimé les lignes nulles du tableau. Faites attention à la ligne totale (Total) et comparez la taille des images complètes (taille FPI) avec la taille des enregistrements ordinaires (taille d'enregistrement).

Le paramètre full_page_writes ne peut être désactivé que si le système de fichiers et le matériel utilisés par eux-mêmes garantissent l'enregistrement atomique. Mais, comme nous pouvons le voir, il n'y a pas de grande raison à cela (en supposant que les sommes de contrôle sont incluses).

Voyons maintenant en quoi la compression est utile.

 => ALTER SYSTEM SET full_page_writes = on; => ALTER SYSTEM SET wal_compression = on; => SELECT pg_reload_conf(); 

Répétez la même expérience.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87710 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26833 latency average = 1.118 ms tps = 894.405027 (including connections establishing) tps = 894.516845 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3CBD3EA8 (1 row) 

Taille du journal:

 => SELECT pg_size_pretty('0/3CBD3EA8'::pg_lsn - '0/3BE87710'::pg_lsn); 
  pg_size_pretty ---------------- 13 MB (1 row) 

Conclusion: en présence d'un grand nombre d'images pleine page (en raison de sommes de contrôle ou de full_page_writes , c'est-à-dire presque toujours), il est très probable d'utiliser la compression malgré le fait que cela charge le processeur.

Performances


Pendant le fonctionnement normal du serveur, un enregistrement séquentiel continu des fichiers journaux se produit. Puisqu'il n'y a pas d'accès aléatoire, les disques durs ordinaires font également face à cette tâche. Mais cette nature de la charge est considérablement différente de la façon dont les fichiers de données sont accessibles.

Par conséquent, il est généralement avantageux de placer le journal sur un disque physique (ou baie de disques) distinct monté sur le système de fichiers du serveur. Au lieu du répertoire $ PGDATA / pg_wal, vous devez créer un lien symbolique vers le répertoire correspondant.

Il existe quelques situations dans lesquelles les fichiers journaux doivent non seulement être écrits, mais également lus. Le premier est un cas compréhensible de récupération après une défaillance. Le second est moins trivial. Il se produit si la réplication en continu est utilisée et que la réplique ne parvient pas à recevoir des entrées de journal alors qu'elles sont encore dans les tampons de RAM du serveur principal. Ensuite, le processus walsender doit lire les données nécessaires sur le disque. Nous en parlerons plus en détail lorsque nous arriverons à la réplication.

La journalisation a lieu dans l'un des deux modes:

  • synchrone - lorsqu'une transaction est validĂ©e, la poursuite du travail est impossible jusqu'Ă  ce que toutes les entrĂ©es de journal concernant cette transaction soient sur le disque;
  • asynchrone - la transaction se termine immĂ©diatement et le journal est Ă©crit en arrière-plan.

Le mode synchrone est déterminé par le paramètre synchronous_commit et est activé par défaut.

Étant donné que la synchronisation est associée à des E / S réelles (c'est-à-dire lentes), il est avantageux de l'exécuter le moins possible. Pour ce faire, le processus de maintenance qui termine la transaction et écrit le journal prend une courte pause, déterminée par le paramètre commit_delay . , commit_siblings . , . , , - .

commit_siblings = 5, commit_delay = 0, . commit_delay , OLTP-.

LSN ( , ). .

( D ACID) — , . , ( COMMIT ) .

, synchronous_commit = off ( local).

wal writer, ( wal_writer_delay = 200ms ).

, , WAL. , , , , . (, : , , .)

, ( ) — ?

, , .

— . : , 3 × wal_writer_delay ( ).

— — .

: ( fsync = off), . , , , .

synchronous_commit . , . , , .

. , WAL. , , .

- , , pgbench.

 => ALTER SYSTEM SET synchronous_commit = off; => SELECT pg_reload_conf(); 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 45439 latency average = 0.660 ms tps = 1514.561710 (including connections establishing) tps = 1514.710558 (excluding connections establishing) 

900 (tps), — 1500. , , , .

. - , . Merci Ă  tous!

, .

Source: https://habr.com/ru/post/fr461523/


All Articles