Outils de défragmentation de table¶
À propos
License | PostgreSQL |
---|---|
Compatibilité | Toutes versions de PostgreSQL |
Résumé¶
Success
Nous recommandons autant que possible d’utiliser VACUUM FULL
ou CLUSTER
s’il faut vraiment reconstruire une table. C’est le plus sûr et le plus rapide.
Dans les cas où une défragmentation en ligne est nécessaire, nous recommandons exclusivement pgcompacttable. En effet, ce projet est activement maintenu et la méthode utilisée pour la défragmentation comporte intrinsèquement moins de risques de corruption.
L’opération sera forcément beaucoup plus longue qu’un VACUUM
.
pg_repack
est répandu mais intrinsèquement plus dangereux.
Il est chaudement conseillé de tester ces outils sur une copie physique de l’instance avant passage en production.
Utilité¶
Le modèle MVCC de PostgreSQL a tendance à mener à de la fragmentation dans les tables.
Un autovacuum
normalement paramétré doit permettre de compenser cela.
Si cela ne suffit pas, les ordres VACUUM FULL
ou CLUSTER
permettent
de reconstruire une table.
Comme ces ordres impliquent un verrouillage complet de la table, et interdisent toute lecture ou écriture dessus, il s’ensuit une indisponibilité souvent longue. De plus, la place disque exigée est parfois excessive car la table est réécrite à côté de l’ancienne et ses index recréés.
Nous présentons ici plusieurs outils pour réaliser cette défragmentation sans interruption de service.
pgcompacttable¶
Auteur | Data Egret |
---|---|
Site du projet | https://github.com/dataegret/pgcompacttable |
Langage | Perl |
Paquets PGDG | Aucun |
Compatibilité | PostgreSQL 9.2 minimum |
Cet outil fonctionne en ligne de commande et permet de diminuer la fragmentation
dans les tables et index sans poser de verrous exclusifs. Il utilise des
ordres SQL (UPDATE
) pour déplacer les données dans les tables, et des commandes REINDEX
pour traiter les index associés.
pgcompacttable
est donc plus sûr et moins consommateur de place,
mais plus lent, que pg_repack
.
Ce mode de fonctionnement est intéressant car il ne contourne pas le moteur de PostgreSQL en le laissant faire lui-même les modifications. Les risques de corruptions des tables et index deviennent nuls. Le besoin en place est limité (l’autovacuum procèdera au nettoyage). Les I/O sont relativement limitées.
Il dispose de nombreuses options pour inclure ou exclure des tables (-t
/-T
),
schémas (-n
/ -N
) ou bases de données (-d
),
ainsi que de filtrer les tables par leur nom (--tables-like 'LIKE expression'
).
L’estimation de la fragmentation est réalisée avec l’extension pgstattuple (fournie avec PostgreSQL) qui est indispensable pour l’utilisation de l’outil.
Les tables sélectionnées ne seront défragmentées que si la fragmentation atteint
20 % de la taille de la table (sauf à utiliser --force
).
Un VACUUM
est effectué au préalable (sauf avec --no-initial-vacuum
).
Par défaut, une ré-indexation de la table suit la réorganisation
(c’est en général nécessaire).
Elle peut également être lancée avant la réorganisation ou tout
simplement ignorée. Les contentions lors de la réindexation
sont réduites grâce au mode concurrent (CONCURRENTLY
).
Des options sont
disponibles pour relancer la réindexation au cas où elle échoue à cause d’un
verrou non obtenu. Elles permettent aussi de contrôler le nombre de tentatives
de ré-indexation, le temps d’attente entre les tentatives et le temps d’attente
avant qu’une réindexation n’échoue (lock_timeout
).
Il est également possible d’afficher les requêtes de ré-indexation
afin de les lancer manuellement (--print-reindex-queries
).
Le contrôle de l’agressivité de l’outil est configurable via le temps d’attente
entre les passes de défragmentation, ou bien en le lançant avec l’utilitaire Linux ionice -c3
.
Si on peut interrompre l’outil n’importe quand, son démarrage est toutefois lent :
il opère un VACUUM
puis une requête assez lourde impliquant pgstattuple
pour rechercher les blocs à défragmenter.
Ce projet est actif.
Pré-requis et installation¶
-
C’est un simple script Perl de Data Egret, à télécharger depuis la page Github.
-
Son fonctionnement dépend de quelques paquets Perl :
- Installer
pgstattuple
dans la base concernée :
Utilisation¶
On utilisera les paramètres habituels pour se connecter à la base de données
(-h
, -p
, -U
, -d
). Les variables d’environnement PG*
ne fonctionnent pas.
Sélectionner les objets à réorganiser :
-a
: réorganisation de tous les objets dans toutes les bases de données-d
: réorganisation de tous les objets dans la base de données spécifiée-n
: réorganisation de tous les objets dans le schéma de la base de données spécifiée-t
: réorganisation de tous les objets dans la table du schéma de la base de données spécifiée-N
: exclusion des objets du schéma spécifié de la réorganisation-T
: exclusion des objets de la table spécifiée de la réorganisation.
Par défaut :
- seules les tables présentant plus de 20% de bloat (par défaut) sont réorganisées (sauf
--force
) - les index sont reconstruits de façon concurrente une fois la réorganisation réalisée, et en cas d’échec l’outil peut rééssayer (100 fois au besoin, par défaut).
Il est possible de désactiver la réindexation post-réorganisation avec l’option --no-reindex
.
Documentation¶
Supervision¶
Surveiller la volumétrie des journaux
En effet, une part substantielle de la table va être réécrite,
et l’autovacuum
va probablement se déclencher.
En cas d’arrêt par Ctrl-C
, contrôler :
- que les index temporaires ont bien été effacés (leur nom commence par
pgcompact_index_
) - que la requête avec
pgstattuple
ne tourne plus (danspg_stat_activity
ou avecpg_activity
) - que les
advisory lock
utilisés sont bien tous fermés (SELECT * FROM pg_locks WHERE locktype = 'advisory'
) - au besoin tuer les sessions avec
SELECT pg_terminate_backend (<n° pid>)
.
Ordres générés¶
Si les traces sont assez verbeuses, on y trouvera entre autres ce qui suit :
- la désactivation des triggers dans la session (en effet, les modifications n’altéreront pas les données, les triggers ne doivent pas se déclencher) :
- la création d’une fonction utilitaire :
LOG: statement:CREATE OR REPLACE FUNCTION public.pgcompact_clean_pages_8185(
CREATE OR REPLACE FUNCTION public.pgcompact_clean_pages_8185(
i_table_ident text,
i_column_ident text,
i_to_page integer,
i_page_offset integer,
i_max_tupples_per_page integer)
RETURNS integer
LANGUAGE plpgsql AS $$
)
…
-
des tentatives d’acquisition de verrou :
-
des VACUUM explicites (pour récupérer tout espace disponible en début de table et éliminer le maximum de lignes mortes) :
- De très nombreux appels de la fonction pour déplacer un ensemble de lignes :
LOG: execute <unnamed>:
SELECT public.pgcompact_clean_pages_8185($1,$2,$3,$4,$5)
2019-06-28 17:04:48 CEST [8186]: [110-1] user=postgres,db=frag,app=[unknown],client=::1 DETAIL: parameters: $1 = '"public"."frag"', $2 = 'f', $3 = '73529', $4 = '5', $5 = '316'
2019-06-28 17:04:48 CEST [8186]: [111-1] user=postgres,db=frag,app=[unknown],client=::1 LOG: duration: 25.116 ms
Exemple 1¶
Dans cet exemple nous compacterons une table nommée frag
ainsi :
L’outil fournit une estimation du compactage et procède à celui-ci :
[Fri Jun 28 17:04:40 2019] (frag) Connecting to database
[Fri Jun 28 17:04:40 2019] (frag) Postgres backend pid: 8186
[Fri Jun 28 17:04:40 2019] (frag) It is recommended to set ionice -c 3 for pgcompacttable: ionice -c 3 -p 8186
[Fri Jun 28 17:04:40 2019] (frag) Handling tables. Attempt 1
[Fri Jun 28 17:04:48 2019] (frag:public.frag) Statistics: 73530 pages (238113 pages including toasts and indexes), it is expected that ~88.370% (64977 pages) can be compacted with the estimated space saving being 507.640MB.
[Fri Jun 28 17:05:48 2019] (frag:public.frag) Progress: 67%, 44130 pages completed.
...
[Fri Jun 28 17:06:25 2019] (frag:public.frag) Processing results: 3677 pages left (11931 pages including toasts and indexes), size reduced by 545.727MB (1.725GB including toasts and indexes) in total.
[Fri Jun 28 17:06:25 2019] (frag) Processing complete.
L’option --print-reindex-queries
fournit le script des
REINDEX
(utile surtout pour les exécuter manuellement si on a demandé --no-reindex
) :
[Fri Jun 28 17:06:23 2019] (frag:public.frag) Reindex: public.frag_pkey, initial size 27422 pages(214.234MB), has been reduced by 94% (203.500MB), duration 0 seconds, attempts 0.
[Fri Jun 28 17:06:23 2019] (frag:public.frag) Reindex queries: public.frag_pkey, initial size 27422 pages (214.234MB), will be reduced by 49% (106.760MB)
[Fri Jun 28 17:06:23 2019] (frag:public.frag) CREATE UNIQUE INDEX CONCURRENTLY pgcompact_index_8185 ON public.frag USING btree (i) TABLESPACE pg_default; --frag
[Fri Jun 28 17:06:23 2019] (frag:public.frag) BEGIN; SET LOCAL statement_timeout TO 1000;
ALTER TABLE "public"."frag" DROP CONSTRAINT "frag_pkey";
ALTER TABLE "public"."frag" ADD CONSTRAINT "frag_pkey" PRIMARY KEY USING INDEX pgcompact_index_8185;
END;; --frag
Exemple 2 (nettoyage forcé)¶
Cet exemple va au plus rapide : pas de réindexation (mais on affiche le SQL
pour réindexer) et pas de VACUUM
préalable :
$ ./pgcompacttable -p 13002 -h monolithe -d pgbench_1000 -U postgres \
-t pgbench_accounts --verbose --no-reindex --print-reindex-queries --no-initial-vacuum
$ ./pgcompacttable -p 13002 -h monolithe -d pgbench_1000 -U postgres -t pgbench_accounts --verbose --no-reindex --print-reindex-queries --no-initial-vacuum
[Fri Feb 25 18:19:04 2022] (pgbench_1000) Connecting to database
[Fri Feb 25 18:19:04 2022] (pgbench_1000) Postgres backend pid: 3091484
[Fri Feb 25 18:19:04 2022] (pgbench_1000) It is recommended to set ionice -c 3 for pgcompacttable: ionice -c 3 -p 3091484
[Fri Feb 25 18:19:04 2022] (pgbench_1000) Handling tables. Attempt 1
[Fri Feb 25 18:19:04 2022] (pgbench_1000:public.pgbench_accounts) Start handling table public.pgbench_accounts
[Fri Feb 25 18:23:03 2022] (pgbench_1000:public.pgbench_accounts) Bloat statistics with pgstattuple: duration 238.368 seconds.
[Fri Feb 25 18:23:03 2022] (pgbench_1000:public.pgbench_accounts) Statistics: 1967097 pages (2515679 pages including toasts and indexes), it is expected that ~16.040% (315530 pages) can be compacted with the estimated space saving being 2.407GB.
[Fri Feb 25 18:23:03 2022] (pgbench_1000:public.pgbench_accounts) Skipping processing: 16.04% space to compact from 20% minimum required.
[Fri Feb 25 18:23:10 2022] (pgbench_1000:public.pgbench_accounts) Reindex queries: public.pgbench_accounts_pkey, initial size 548044 pages (4.181GB), will be reduced by 41% (1.721GB)
[Fri Feb 25 18:23:10 2022] (pgbench_1000:public.pgbench_accounts) CREATE UNIQUE INDEX CONCURRENTLY pgcompact_index_2205715 ON public.pgbench_accounts USING btree (aid) TABLESPACE pg_default; --pgbench_1000
[Fri Feb 25 18:23:10 2022] (pgbench_1000:public.pgbench_accounts) BEGIN; SET LOCAL statement_timeout TO 1000;
ALTER TABLE "public"."pgbench_accounts" DROP CONSTRAINT "pgbench_accounts_pkey";
ALTER TABLE "public"."pgbench_accounts" ADD CONSTRAINT "pgbench_accounts_pkey" PRIMARY KEY USING INDEX pgcompact_index_2205715;
END;; --pgbench_1000
[Fri Feb 25 18:23:10 2022] (pgbench_1000:public.pgbench_accounts) Finish handling table public.pgbench_accounts
[Fri Feb 25 18:23:10 2022] (pgbench_1000) Processing complete.
[Fri Feb 25 18:23:10 2022] (pgbench_1000) Processing results: size reduced by 0.000B (0.000B including toasts and indexes) in total.
[Fri Feb 25 18:23:10 2022] (pgbench_1000) Disconnecting from database
[Fri Feb 25 18:23:10 2022] Processing complete: 1 retries to process has been done
[Fri Feb 25 18:23:10 2022] Processing results: size reduced by 0.000B (0.000B including toasts and indexes) in total.
pgcompact¶
Auteurs | Sergey Konoplev, Maxim Bogukt |
---|---|
Site du projet | https://github.com/grayhemp/pgtoolkit |
Langage | Perl |
Attention
Cet outil n’est plus maintenu.
Cet outil s’utilise en ligne de commande et comporte beaucoup de similitudes
avec pgcompacttable.
Il se repose aussi sur des commandes SQL pour réorganiser les tables et des commandes
REINDEX
pour les index.
Malheureusement, il n’est pas maintenu depuis 2015, c’est pourquoi nous ne recommandons pas son utilisation.
pg_repack¶
Attention
Nous ne recommandons pas l’utilisation de cet outil qui par sa nature est très intrusif et pourrait être dangereux.
Cela dit, il est utilisé assez largement depuis longtemps et sa maintenance est active.
Auteur | Data Egret |
---|---|
Site du projet | https://reorg.github.io/pg_repack/ |
Page Github | https://github.com/reorg/pg_repack |
Langage | C |
Paquets PGDG | postgresql-16-repack (Debian & Ubuntu), pg_repack_16 et pg_repack_16-llvmjit (RPM) |
Compatibilité | PostgreSQL 9.4 minimum |
Le principe de pg_repack
est de reconstruire la table à côté
de l’ancienne tout en permettant à la production de continuer
(c’est-à-dire sans verrouillage complet et long des tables).
pg_repack
est donc plus rapide mais plus consommateur de place que pgcompacttable
.
Il prend un verrou assez lourd sur la table, crée une table temporaire, ajoute des triggers sur la table originale pour récupérer les lignes modifiées pendant la reconstruction. La copie finie, il échange les tables.
C’est un outil de bas niveau et intrusif qui utilise des triggers et modifie directement le catalogue système, ce qui rendrait catastrophique tout bug : corruption, fichiers orphelins… (voir les issues Github).
Des clés primaires (vraie PK
ou UNIQUE NOT NULL
) sont obligatoires.
Pour aller plus loin¶
- Understanding pg_repack: What Can Go Wrong – and How to Avoid It (Blog Percona, 2021),
sur les dangers de l’abus de
pg_repack
, les interactions avec d’autres sessions, et des précautions à prendre, avec une analyse du fonctionnement.