Formation DBA2
Dalibo SCOP
25.09
5 septembre 2025
| Formation | Formation DBA2 |
| Titre | PostgreSQL Avancé |
| Révision | 25.09 |
| ISBN | N/A |
| https://dali.bo/dba2_pdf | |
| EPUB | https://dali.bo/dba2_epub |
| HTML | https://dali.bo/dba2_html |
| Slides | https://dali.bo/dba2_slides |
Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :
PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.
Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 13 à 17.
PostgreSQL est :
# ps f -e --format=pid,command | grep -E "postgres|postmaster"
96122 /usr/pgsql-17/bin/postmaster -D /var/lib/pgsql/15/data/
96123 \_ postgres: logger
96125 \_ postgres: checkpointer
96126 \_ postgres: background writer
96127 \_ postgres: walwriter
96128 \_ postgres: autovacuum launcher
96131 \_ postgres: logical replication launcher(sous Rocky Linux 8)
postmasterbackground writercheckpointerwalwriterautovacuum launcherstats collector (avant v15)logical replication launchermax_connections (défaut : 100) - connexions réservées
Structure de la mémoire sous PostgreSQL
work_mem)postgres$ ls $PGDATA
base pg_ident.conf pg_stat pg_xact
current_logfiles pg_logical pg_stat_tmp postgresql.auto.conf
global pg_multixact pg_subtrans postgresql.conf
log pg_notify pg_tblspc postmaster.opts
pg_commit_ts pg_replslot pg_twophase postmaster.pid
pg_dynshmem pg_serial PG_VERSION
pg_hba.conf pg_snapshots pg_walpostgresql.conf ( + fichiers inclus)postgresql.auto.confpg_hba.conf ( + fichiers inclus (v16))pg_ident.conf (idem)PG_VERSION : fichier contenant la version majeure de
l’instancepostmaster.pid
external_pid_filepostmaster.optsbase/ : contient les fichiers de données
pgsql_tmp : fichiers temporairesglobal/ : contient les objets globaux à toute
l’instancepg_wal/ : journaux de transactions
archive_status00000002 00000142 000000FFpg_xact/ : état des transactionspg_commit_ts/, pg_multixact/,
pg_serial/ \ pg_snapshots/,
pg_subtrans/, pg_twophase/pg_logical/pg_repslot/pg_tblspc/ : tablespaces
Statistiques d’activité :
stats collector (≤v14) & extensionspg_stat_tmp/ : temporairespg_stat/ : définitifpg_dynshmem/pg_notify/PostgreSQL est complexe, avec de nombreux composants
Une bonne compréhension de cette architecture est la clé d’une bonne administration.
Pour aller (beaucoup) plus loin :
N’hésitez pas, c’est le moment !
initdbblock_size : 8 kowal_block_size : 8 kosegment_size : 1 Gowal_segment_size : 16 Mo (option
--wal-segsize d’initdb en v11)postgresql.conf ( + fichiers inclus)postgresql.auto.confpg_hba.conf ( + fichiers inclus (v16))pg_ident.conf (idem)Fichier principal de configuration :
/var/lib/…)/etc/postgresql/<version>/<nom>/postgresql.confclé = valeurinclude,
include_if_existsALTER SYSTEM SET … ( renseigne
postgresql.auto.conf )pg_ctlALTER DATABASE | ROLE … SET paramètre = …SET / SET LOCALSHOWpg_settingspg_file_settingspg_hba.conf (Host Based Authentication)pg_ident.conf : si mécanisme externe
d’authentificationhba_file et ident_file-- déclaration
CREATE TABLESPACE ssd LOCATION '/mnt/ssd/pg';
-- droit pour un utilisateur
GRANT CREATE ON TABLESPACE ssd TO un_utilisateur ;
-- pour toute une base
CREATE DATABASE nomdb TABLESPACE ssd;
ALTER DATABASE nomdb SET default_tablespace TO ssd ;
-- pour une table
CREATE TABLE une_table (…) TABLESPACE ssd ;
ALTER TABLE une_table SET TABLESPACE ssd ; -- verrou !
-- pour un index (pas automatique)
ALTER INDEX une_table_i_idx SET TABLESPACE ssd ;CREATE TABLESPACE ssd LOCATION '/mnt/data_ssd/' ;
CREATE TABLESPACE ssd_tmp1 LOCATION '/mnt/temp1' ;
CREATE TABLESPACE ssd_tmp2 LOCATION '/mnt/temp2' ;
GRANT CREATE ON TABLESPACE ssd TO dupont ;
GRANT CREATE ON TABLESPACE ssd_tmp1,ssd_tmp2 TO dupont ;default_tablespacetemp_tablespaces :
L’accès à la base se fait par un protocole réseau clairement défini :
Les demandes de connexion sont gérées par le postmaster.
Paramètres : port, listen_adresses,
unix_socket_directories, unix_socket_group et
unix_socket_permissions
tcp_keepalives_idletcp_keepalives_intervaltcp_keepalives_countclient_connection_check_interval (v14)ssl, ssl_ciphers,
ssl_renegotiation_limittrack_activities,
track_activity_query_sizetrack_counts, track_io_timing et
track_functionsupdate_process_titlestats_temp_directory (< v15)INSERT, SELECT…) par table
et indexpg_stat_user_*pg_statio_user_*pg_stat_activity (requêtes)pg_stat_bgwriter, pg_stat_checkpointer
(v17+)pg_lockspg_stat_reset_shared()pg_stats
default_statistics_target)ANALYZE tableSQL est un langage déclaratif :
seq_page_cost, random_page_cost,
cpu_tuple_cost, cpu_index_tuple_cost,
cpu_operator_costparallel_setup_cost,
parallel_tuple_costeffective_cache_sizejoin_collapse_limitfrom_collapse_limitgeqo & geqo_threshold (≥ 12
tables)plan_cache_modeconstraint_exclusionenable_partition_pruningcursor_tuple_fractionsynchronize_seqscansN’hésitez pas, c’est le moment !
La mémoire & PostgreSQL :
shared_buffers
wal_buffers
max_connections
track_activity_query_size
max_connections,
max_locks_per_transactionwork_mem
hash_mem_multipliermaintenance_work_mem
autovacuum_work_memtemp_buffersShared buffers ou blocs de mémoire partagée
effective_cache_size ( ~⅔ RAM)But : Le ring buffer permet de ne pas purger le cache à cause :
VACUUM (écritures)COPY, CREATE TABLE AS SELECT…vacuum_buffer_usage_limit2 extensions en « contrib » :
pg_buffercachepg_prewarmPour synchroniser les blocs « dirty » :
COMMITEssentiellement :
pg_wal/ : journaux de transactions
archive_status00000002 00000142 000000FFpg_xact/ : état des transactionspg_waldump ou pg_walinspect start_lsn | record_type | description | block_ref
------------+-------------+----------------------+----------------
89/3B6B6868 | INSERT_LEAF | off: 1 | …1663/5/1673829
89/3B6B68A8 | COMMIT | 2025-01-20 16:40:30. | ø
89/3B6B68D0 | INSERT | off: 2, flags: 0x00 | …1663/5/1673826
89/3B6B6910 | INSERT_LEAF | off: 2 | …1663/5/1673829
89/3B6B6950 | ABORT | 2025-01-20 16:40:30. | ø
89/3B6B6978 | UPDATE | old_xmax: 34696089, | …1663/5/1673826
89/3B6B69C0 | INSERT_LEAF | off: 3 | …1663/5/1673829
89/3B6B6A00 | COMMIT | 2025-01-20 16:40:30. | øcheckpointercheckpoint_timeoutmax_wal_size (pas un plafond !)CHECKPOINTcheckpoint_completion_target × durée moy. entre 2
checkpointscheckpoint_warninglog_checkpointsNettoyage selon l’activité, en plus du
checkpointer :
bgwriter_delaybgwriter_lru_maxpagesbgwriter_lru_multiplierbgwriter_flush_afterwalwriterwal_bufferswal_writer_flush_afterfsync = onfull_page_writes = onwal_compression
off (défaut)pglz (on), lz4,
zstd (v15)synchronous_commit
commit_delay / commit_siblingswal_level, archive_modearchive_command ou archive_libraryMémoire et journalisation :
N’hésitez pas, c’est le moment !
PostgreSQL utilise un modèle appelé MVCC (Multi-Version Concurrency Control).
ROLLBACK
instantanéBEGIN ISOLATION LEVEL xxx;READ COMMITTEDctid = (bloc, item dans le bloc)Table initiale :
| xmin | xmax | Nom | Solde |
|---|---|---|---|
| 100 | M. Durand | 1500 | |
| 100 | Mme Martin | 2200 |
| xmin | xmax | Nom | Solde |
|---|---|---|---|
| 100 | 150 | M. Durand | 1500 |
| 100 | Mme Martin | 2200 | |
| 150 | M. Durand | 1300 |
| xmin | xmax | Nom | Solde |
|---|---|---|---|
| 100 | 150 | M. Durand | 1500 |
| 100 | 150 | Mme Martin | 2200 |
| 150 | M. Durand | 1300 | |
| 150 | Mme Martin | 2400 |
| xmin | xmax | Nom | Solde |
|---|---|---|---|
| 100 | 150 | M. Durand | 1500 |
| 100 | 150 | Mme Martin | 2200 |
| 150 | M. Durand | 1300 | |
| 150 | Mme Martin | 2400 |
COMMIT ou ROLLBACK très rapideROLLBACK instantanéVACUUMxmin/xmaxAprès 4 milliards de transactions :
Concrètement ?
VACUUM FREEZE
HOT = Heap-Only Tuples
VACUUMVACUUM et VACUUM FREEZELa gestion des verrous est liée à l’implémentation de MVCC
PostgreSQL possède un gestionnaire de verrous
pg_locksxmaxpg_locks :
max_locks_per_transaction (+ paramètres pour la
sérialisation)lock_timeout (éviter l’empilement des verrous)deadlock_timeout (défaut 1 s)log_lock_waitsDivers seuils possibles, jamais globalement.
| Paramètre | Cible du seuil |
|---|---|
lock_timeout |
Attente de verrou |
statement_timeout |
Ordre en cours |
idle_session_timeout |
Session inactive |
idle_in_transaction_session_timeout |
Transaction en cours, inactive |
transaction_timeout
(v17) |
Transaction en cours |
TOAST : The Oversized-Attribute Storage Technique
Que faire si une ligne dépasse d’un bloc ?
PLAIN/MAIN/EXTERNAL/EXTENDEDpg_toast_XXX
SELECT *VACUUM et le processus
d’arrière-plan autovacuum.N’hésitez pas, c’est le moment !
VACUUMVACUUM seul, ANALYZE,
FULL, FREEZE
VACUUM
VACUUM (SQL)vacuumdb (shell, pour appels en masse)autovacuum (seuils)NB : L’espace est rarement rendu à l’OS !
Ne pas confondre :
VACUUM seul
ANALYZE
VACUUM (ANALYZE)
VACUUM (FREEZE)
VACUUM FULL
PARALLELVACUUM (BUFFER_USAGE_LIMIT 2MB)vacuum_buffer_usage_limitSKIP_DATABASE_STATS, ONLY_DATABASE_STATS
(v16+)SKIP_LOCKEDSET lock_timeout = '1s'VERBOSE
Ponctuellement :
DISABLE_PAGE_SKIPPINGpg_stat_activity ou toppg_stat_user_tables
last_vacuum / last_autovacuumlast_analyze / last_autoanalyzelog_autovacuum_min_durationPour VACUUM simple / VACUUM FREEZE
pg_stat_progress_vacuumPartie ANALYZE
pg_stat_progress_analyzeManuel ou via autovacuum
Pour VACUUM FULL
pg_stat_progress_clusterVACUUMVACUUM, ANALYZE,
FREEZE
FULLautovacuum (on !)autovacuum_naptime (1 min)autovacuum_max_workers (3)
Seuil de déclenchement =
threshold + scale factor × nb lignes de la table
VACUUM
autovacuum_vacuum_scale_factor (20 %)autovacuum_vacuum_threshold (50)autovacuum_vacuum_insert_threshold (1000)autovacuum_vacuum_insert_scale_factor (20 %)ANALYZE
autovacuum_analyze_scale_factor (10 %)autovacuum_analyze_threshold (50)| VACUUM manuel | autovacuum |
|---|---|
| Urgent | Arrière-plan |
| Pas de limite | Peu agressif |
| Paramètres | Les mêmes + paramètres de surcharge |
maintenance_work_memautovacuum_work_memVACUUMvacuum_cost_page_hit/_miss/_dirty
(1/ 10 ou 2 /20)vacuum_cost_limit (200)vacuum_cost_delay (en manuel : 0 ms !)autovacuum_vacuum_cost_limit (identique)autovacuum_vacuum_cost_delay (2 ms)Le but est de geler les numéros de transaction assez vite :
Quand le VACUUM gèle-t-il les lignes ?
age ( pgclass.relfrozenxid )
vacuum_freeze_min_age (50 Mtrx)
vacuum_freeze_table_age (150 Mtrx)
autovacuum_freeze_max_age (200 Mtrx)pg_cancel_backend + VACUUM FREEZE
manuelWARNING: database "mydb" must be vacuumed within 39985967 transactions
HINT: To avoid XID assignment failures, execute a database-wide VACUUM in that database.
ERROR: database is not accepting commands that assign new XIDs to avoid wraparound data loss in database "mydb"
HINT: Execute a database-wide VACUUM in that database.
pg_prepared_xacts)VACUUMcheck_pg_activity : xmin,
max_freeze_ageVACUUM FREEZE préventif en période de maintenance“Vacuuming is like exercising.
If it hurts, you’re not doing it enough!”(Robert Haas, PGConf.EU 2023, Prague, 13 décembre 2023)
last_(auto)analyze /
last_(auto)vacuumALTER TABLE table_name SET (autovacuum_analyze_scale_factor = 0.01) ;
ALTER TABLE table_name SET (autovacuum_vacuum_threshold = 1000000) ;vacuumdb
quotidienFREEZE brutal après migration logique ou gros
import
VACUUM FULL : dernière extrémitéVACUUM fait de plus en plus de choses au fil des
versionsN’hésitez pas, c’est le moment !
Le partitionnement déclaratif apparaît avec PostgreSQL 10
Préférer un PostgreSQL récent
Ne plus utiliser l’ancien partitionnement par héritage.
VACUUM (FULL), réindexation, déplacements,
sauvegarde logique…pg_dump parallélisable
CREATE TABLE logs ( d timestamptz, contenu text) PARTITION BY RANGE (d) ;
CREATE TABLE logs_201901 PARTITION OF logs
FOR VALUES FROM ('2019-01-01') TO ('2019-02-01');
CREATE TABLE logs_201902 PARTITION OF logs
FOR VALUES FROM ('2019-02-01') TO ('2019-03-01');
…
CREATE TABLE logs_201912 PARTITION OF logs
FOR VALUES FROM ('2019-12-01') TO ('2020-01-01');
…
CREATE TABLE logs_autres PARTITION OF logs
DEFAULT ; -- pour ne rien perdre
DROP ou DETACHpg_dump à chaudpg_basebackuppg_receivewalCOMMIT), intégrité,
durabilitépg_wal/ si échec d’archivage… et arrêt si
plein !(Non PITR)
2 étapes :
pg_receivewalpg_basebackuparchiverpg_receivewal (flux de réplication)Préalables :
postgresql.conf :
wal_level = replicaarchive_mode = on (ou always)La commande d’archivage :
postgresql.conf :
archive_command = '… une commande …'archive_library = '… une bibliothèque …'
(v15+)archive_command :archive_command='cp %p /mnt/nfs1/archivage/%f && sync /mnt/nfs1/'
archive_command='test ! -f /arch/%f && cp %p /arch/%f'
archive_command='/usr/bin/rsync -az %p postgres@10.9.8.7:/archives/%f'
archive_command='/opt/mon_script.sh %p %f'
archive_command='/usr/bin/pgbackrest --stanza=prod archive-push %p'
archive_command='/usr/bin/barman-wal-archive backup prod %p'
archive_command='/bin/true' # désactivationpostgresql.conf (suite) :
archive_timeout = '… min'wal_level et/ou
archive_modepg_stat_archiverpg_wal/archive_status/
.ready et .donepg_wal
pg_receivewal s’arrêtepg_receivewal tente de se
reconnecterpg_receivewal
SELECT pg_backup_start (
un_label : textefast : forcer un checkpoint ?)
rsync et autres outilspostmaster.pid, log, pg_wal,
pg_replslot et quelques autresNe pas oublier !!
SELECT * FROM pg_backup_stop (
true : attente de l’archivage)
Simple, mais à appliquer rigoureusement
pg_wal, postmaster.pid,
log/recovery.signalrestore_command = '… une commande …'pg_wal/postgresql.[auto.]confrecovery_target_name,
recovery_target_timerecovery_target_xid,
recovery_target_lsnrecovery_target_inclusiverecovery_target_timeline : latest (en
général)recovery_target_action : pausepg_wal_replay_resume pour ouvrir immédiatementrecovery.signal volontairement00000002000000000000000C).historyrecovery_target_timeline
latest
checkpoint_timeoutmax_wal_sizewal_compression = on
pglz (on), lz4,
zstd (v15+)gzip,
bzip2, lzma…
barman)list-server, backup,
list-backup, recover…pg_receivewalpg_dumpN’hésitez pas, c’est le moment !
pg_walpg_resetwalpg_surgeryQuelques pistes (liste non exhaustive)
fsync est-il bien honoré de l’OS au disque ?
(batteries !)sar, atop…fsyncfull_page_writeinitdb --data-checksums (création)pg_checksums --enable (à posteriori)pg_checksums --checkkill -9, rm -rf,
rsync, find … -exec …pg_dumppg_dumpallCOPYpsql / pg_restore
--section=pre-data / data /
post-datapg_checksums --checkpg_dump, pg_dumpallpg_basebackup, voire
pg_basebackup --target=blackholePour les index :
amcheck : vérification uniquement
pg_amcheck (v14+)
REINDEXREINDEXpg_relation_filepath()ctid /
pageinspectddpg_walpg_resetwal permet de forcer le démarrageglobal/pg_controlpg_resetwal… parfoispg_xact