Extensions d’observation et de diagnostic pour les DBA : pgstattuple, pageinspect, pgrowlocks, cache et pgAudit.
DBA42
3/ Extensions PostgreSQL pour les DBA
3.1 Préambule
Nombreuses extensions pour observer le comportement de PostgreSQL
Contribs ou projets externes
De nombreux permettent de manipuler une facette de PostgreSQL à laquelle on n’a normalement pas accès. Leur utilisation est parfois très spécialisée et pointue.
3.2 pgstattuple
pgstattuple fournit une mesure (par parcours complet de l’objet) sur:
Pour une table
remplissage des blocs
enregistrements morts
espace libre
Pour un index
profondeur de l’index
remplissage des feuilles
fragmentation (feuilles non consécutives)
Cette extension est fournie avec le serveur PostgreSQL (« contrib »).
Comme chaque interrogation nécessite une lecture complète de l’objet, ces fonctions ne sont pas à appeler en supervision.
Elles servent de façon ponctuelle pour s’assurer qu’un objet nécessite une réorganisation. Ici, l’index dspam_token_data_uid_key pourrait certainement être reconstruit… il deviendrait 40 % plus petit environ (remplissage à 51 % au lieu de 90 %).
leaf_fragmentation indique le pourcentage de pages feuilles qui ne sont pas physiquement contiguës sur le disque. Cela peut être important dans le cas d’un index utilisé pour des Range Scans (requête avec des inégalités), mais n’a aucune importance ici puisqu’il s’agit d’une clé primaire technique, donc d’un index qui n’est interrogé que pour récupérer des enregistrements de façon unitaire.
3.3 pg_freespacemap
La freespacemap :
est renseignée par VACUUM, par objet (table/index)
est consommée par les sessions modifiant des données (INSERT/UPDATE)
est interrogée la freespacemap pour connaître l’espace libre
est rarement utilisée (doute sur l’efficacité de VACUUM)
Cette extension est fournie avec le serveur PostgreSQL (« contrib »).
Voici deux exemples d’utilisation de pg_freespacemap :
L’interprétation de « avail » est un peu complexe, et différente suivant qu’on inspecte une table ou un index. Il est préférable de se référer à la documentation.
3.4 pg_visibility
La Visibility Map :
Est renseignée par VACUUM, par table
Permet de savoir que l’ensemble des enregistrements de ce bloc est visible
Indispensable pour les parcours d’index seul
Interroger la visibility map permet de voir si un bloc est :
visible
gelé
Rarement utilisé
Cette extension est fournie avec le serveur PostgreSQL (« contrib »).
blkno | all_visible | all_frozen | pd_all_visible
-------+-------------+------------+----------------
0 | f | f | f
1 | f | f | f
Les deux blocs que composent la table test_visibility sont à false, ce qui est normal puisque l’opération de vacuum n’a jamais été exécutée sur cette table.
On lance donc une opération de vacuum :
VACUUMVERBOSEtest_visibility;
INFO: exécution du VACUUM sur « public.test_visibility »
INFO: « test_visibility » : 0 versions de ligne supprimables,
451 non supprimables
parmi 2 pages sur 2
DÉTAIL : 0 versions de lignes mortes ne peuvent pas encore être supprimées.
Il y avait 0 pointeurs d éléments inutilisés.
Ignore 0 page à cause des verrous de blocs.
0 page est entièrement vide.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
Vacuum voit bien nos 451 lignes, et met donc la visibility map à jour. Lorsqu’on la consulte, on voit bien que toutes les lignes sont visibles de toutes les transactions :
SELECT*FROMpg_visibility(33259);
blkno | all_visible | all_frozen | pd_all_visible
-------+-------------+------------+----------------
0 | t | f | t
1 | t | f | t
La colonne all_frozen passera à t après un VACUUM FREEZE.
3.5 pageinspect
Vision du contenu d’un bloc
Sans le dictionnaire, donc sans décodage des données
Affichage brut
Utilisé surtout en debug, ou dans les cas de corruption
Fonctions de décodage pour les tables, les index (B-tree, hash, GIN, GiST), FSM
Nécessite de connaître le code de PostgreSQL
Cette extension est fournie avec le serveur PostgreSQL (« contrib »).
Nous savons donc que l’enregistrement (0,2) est verrouillé par le pid 928. Nous avons le mode de verrouillage, le (ou les) numéro de transaction associés. Un enregistrement peut être verrouillé par plus d’une transaction dans le cas d’un SELECT FOR SHARE. Dans ce cas, PostgreSQL crée un « multixact » qui est stocké dans locker, multi vaut true, et xids contient plus d’un enregistrement. C’est un cas très rare d’utilisation.
3.7 Gestion du cache
pg_buffercache : voir ce qu’il y a dans mes shared buffers
pg_prewarm : forcer le chargement du cache
Ces extensions sont fournies avec le serveur PostgreSQL (« contrib »).
pg_buffercache et pg_prewarm sont des extensions déjà connues de beaucoup de DBA.
Rappelons que pg_buffercache permet de lister chaque bloc dans le cache de PostgreSQL, et de savoir notamment s’il est dirty.
pg_prewarm est lui très utile pour forcer le chargement d’un objet dans le cache de PostgreSQL ou de l’OS, y compris automatiquement au démarrage.
3.8 pgAudit
Extension disponible via le PGDG
Pour tracer les accès aux objets
Pour les audits & certifications
Format CSV dans postgresql.log
pgAudit trace les requêtes, les accès aux tables et divers événements et peut également servir dans le cadre d’audits ou de certifications.
Cette extension, écrite en C, utilise des event triggers et du code supplémentaire pour les DDL. Les événements sont dirigés vers les traces de PostgreSQL (postgresql.log ou équivalent), sous un format CSV.
Cette extension est à installer depuis les paquets du PGDG ou à compiler depuis le dépôt Github.
La sortie se fait au format CSV dans postgresql.log. Exemple :
LOG: AUDIT: SESSION,792,1,MISC,BEGIN,,,BEGIN,<not logged>
LOG: AUDIT: SESSION,793,1,WRITE,UPDATE,,,UPDATE pgbench_accounts SET abalance = abalance + 3043 WHERE aid = 394881,<not logged>
LOG: AUDIT: SESSION,794,1,READ,SELECT,,,SELECT abalance FROM pgbench_accounts WHERE aid = 394881,<not logged>
LOG: AUDIT: SESSION,795,1,WRITE,UPDATE,,,UPDATE pgbench_tellers SET tbalance = tbalance + 3043 WHERE tid = 49,<not logged>
LOG: AUDIT: SESSION,796,1,WRITE,UPDATE,,,UPDATE pgbench_branches SET bbalance = bbalance + 3043 WHERE bid = 3,<not logged>
LOG: AUDIT: SESSION,797,1,WRITE,INSERT,,,"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (49, 3, 394881, 3043, CURRENT_TIMESTAMP)",<not logged>
LOG: AUDIT: SESSION,798,1,MISC,COMMIT,,,END,<not logged>
…
LOG: AUDIT: SESSION,4,1,DDL,CREATE TABLE,TABLE,public.pgbench_accounts,"create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=100)",<not logged>
…
LOG: AUDIT: SESSION,18,1,DDL,ALTER TABLE,TABLE,public.pgbench_accounts,alter table pgbench_accounts add primary key (aid),<not logged>
…
LOG: AUDIT: SESSION,20,1,DDL,ALTER TABLE,,,alter table pgbench_accounts add constraint pgbench_accounts_bid_fkey foreign key (bid) references pgbench_branches,<not logged>
LOG: AUDIT: SESSION,20,2,READ,SELECT,,,"SELECT fk.""bid"" FROM ONLY ""public"".""pgbench_accounts"" fk LEFT OUTER JOIN ONLY ""public"".""pgbench_branches"" pk ON ( pk.""bid"" OPERATOR(pg_catalog.=) fk.""bid"") WHERE pk.""bid"" IS NULL AND (fk.""bid"" IS NOT NULL)",<not logged>
LOG: AUDIT: SESSION,20,1,DDL,ALTER TABLE,TABLE,public.pgbench_accounts,alter table pgbench_accounts add constraint pgbench_accounts_bid_fkey foreign key (bid) references pgbench_branches,<not logged>
…
LOG: AUDIT: SESSION,1,1,DDL,DROP TABLE,TABLE,public.pgbench_tellers,"drop table if exists pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers",<not logged>
…
LOG: AUDIT: SESSION,12,1,MISC,VACUUM,,,vacuum analyze pgbench_branches,<not logged>
Attention, le volumé généré peut devenir colossal !