PostgreSQL n'utilise pas le mécanisme de mise à jour IN-PLACE, donc selon la façon dont les commandes DELETE et UPDATE sont conçues,
- Chaque fois que des opérations DELETE sont effectuées, il marque le tuple existant comme DEAD au lieu de supprimer physiquement ces tuples.
- De même, chaque fois que l'opération UPDATE est effectuée, elle marque le tuple existant correspondant comme DEAD et insère un nouveau tuple (c'est-à-dire l'opération UPDATE =DELETE + INSERT).
Ainsi, chaque commande DELETE et UPDATE se traduira par un tuple DEAD, qui ne sera jamais utilisé (sauf s'il y a des transactions parallèles). Ces tuples morts entraîneront une utilisation inutile de l'espace supplémentaire, même si le nombre d'enregistrements effectifs est égal ou inférieur. Ceci est également appelé gonflement de l'espace dans PostgreSQL. Étant donné que PostgreSQL est largement utilisé comme système de base de données relationnelle de type OLTP, où de fréquentes opérations INSERT, UPDATE et DELETE sont effectuées, il y aura de nombreux tuples DEAD et donc les conséquences correspondantes. PostgreSQL a donc nécessité un mécanisme de maintenance solide pour gérer ces tuples DEAD. VACUUM est le processus de maintenance qui prend en charge le tuple DEAD ainsi que quelques activités supplémentaires utiles pour optimiser le fonctionnement de VACUUM. Comprenons quelques termes à utiliser plus tard dans ce blog.
Carte de visibilité
Comme son nom l'indique, il conserve les informations de visibilité sur les pages contenant uniquement des tuples connus pour être visibles par toutes les transactions actives. Pour chaque page, un bit est utilisé. Si le bit est mis à 1 signifie que tous les tuples de la page correspondante sont visibles. Le bit défini sur 0 signifie qu'il n'y a pas d'espace libre sur la page donnée et que les tuples peuvent être visibles pour toutes les transactions.
La carte de visibilité est maintenue pour chaque relation (table et index) et est associée aux relations principales, c'est-à-dire que si le nom du nœud du fichier de relation est 12345, alors le fichier de visibilité est stocké dans le fichier parallèle 12345_vm.
Carte de l'espace libre
Il conserve les informations sur l'espace libre contenant des détails sur l'espace disponible dans la relation. Ceci est également stocké dans le fichier parallèle au fichier principal de relation, c'est-à-dire que si le nom du nœud du fichier de relation est 12345, alors le fichier de carte d'espace libre est stocké dans le fichier parallèle 12345_fsm.
Geler le tuple
PostgreSQL utilise 4 octets pour stocker l'identifiant de transaction, ce qui signifie qu'un maximum de 2 milliards de transactions peuvent être générées avant qu'il ne se termine. Maintenant, considérez encore qu'à ce moment, un tuple contient l'identifiant de transaction initial, disons 100, puis pour la nouvelle transaction (qui utilise la transaction enveloppée), disons 5, l'identifiant de transaction 100 se penchera sur l'avenir et il ne pourra pas voir les données ajoutées / modifié par celui-ci même si c'était en fait dans le passé. Afin d'éviter cet identifiant de transaction spécial, FrozenTransactionId (égal à 2) est attribué. Cet identifiant de transaction spécial est toujours considéré comme appartenant au passé et sera visible pour toutes les transactions.
VIDE
Le travail principal de VACUUM consiste à récupérer l'espace de stockage occupé par les tuples DEAD. L'espace de stockage récupéré n'est pas restitué au système d'exploitation, il est simplement défragmenté dans la même page, de sorte qu'il est simplement disponible pour être réutilisé par une future insertion de données dans la même table. Pendant que l'opération VACUUM se déroule sur une table particulière, une autre opération READ/WRITE peut être effectuée simultanément sur la même table car le verrou exclusif n'est pas pris sur la table particulière. Dans le cas où un nom de table n'est pas spécifié, VACUUM sera effectué sur toutes les tables de la base de données. L'opération VACUUM effectue ci-dessous une série d'opérations dans un verrou ShareUpdateExclusive :
- Analyser toutes les pages de toutes les tables (ou table spécifiée) de la base de données pour obtenir tous les tuples morts.
- Gelez les anciens tuples si nécessaire.
- Supprimez le tuple d'index pointant vers les tuples DEAD respectifs.
- Supprimer les tuples DEAD d'une page correspondant à une table spécifique et réallouer les tuples actifs dans la page.
- Mettre à jour la carte de l'espace libre (FSM) et la carte de visibilité (VM).
- Tronquez la dernière page si possible (s'il y avait des tuples DEAD qui ont été libérés).
- Mettre à jour toutes les tables système correspondantes.
Comme nous pouvons le voir dans les étapes de travail ci-dessus pour VACUUM, il est clair qu'il s'agit d'une opération très coûteuse car elle doit traiter toutes les pages de la relation. Il est donc très nécessaire de sauter les pages possibles qui ne nécessitent pas d'être aspirées. Étant donné que la carte de visibilité (VM) donne des informations sur la page où s'il n'y a pas d'espace libre, on peut supposer que le vide de page correspondant n'est pas requis et donc cette page peut être ignorée en toute sécurité.
Étant donné que VACUUM parcourt de toute façon toutes les pages et tous leurs tuples, il en profite pour effectuer une autre tâche importante consistant à geler les tuples qualifiants.
VIDE COMPLET
Comme indiqué dans la section précédente, même si VACUUM supprime tous les tuples DEAD et défragmente la page pour une utilisation future, cela n'aide pas à réduire le stockage global de la table car l'espace n'est en fait pas libéré pour le système opérateur. Supposons qu'une table tbl1 que le stockage total ait atteint 1,5 Go et que sur ce 1 Go soit occupé par un tuple mort, puis après VACUUM, environ 1 Go supplémentaire sera disponible pour une nouvelle insertion de tuple, mais le stockage total restera à 1,5 Go.
Full VACUUM résout ce problème en libérant de l'espace et en le renvoyant au système d'exploitation. Mais cela a un coût. Contrairement à VACUUM, FULL VACUUM n'autorise pas le fonctionnement en parallèle car il prend un verrou exclusif sur la relation qui reçoit FULL VACUUM. Voici les étapes :
- Prend un verrou exclusif sur la relation.
- Créer un fichier de stockage parallèle vide.
- Copiez tous les tuples actifs du stockage actuel vers le stockage nouvellement alloué.
- Libérez ensuite l'espace de stockage d'origine.
- Libérez le verrou.
Ainsi, comme il ressort également des étapes, il n'aura de stockage que pour les données restantes.
VIDE automatique
Au lieu de faire VACUUM manuellement, PostgreSQL supporte un démon qui déclenche automatiquement VACUUM périodiquement. Chaque fois que VACUUM se réveille (par défaut 1 minute), il invoque plusieurs travaux (selon les processus de configuration autovacuum_worker).
Les agents de vide automatique effectuent des processus VACUUM simultanément pour les tables désignées respectives. Étant donné que VACUUM ne prend aucun verrou exclusif sur les tables, il n'a pas (ou peu) d'impact sur les autres travaux de la base de données.
La configuration d'Auto-VACUUM doit être effectuée en fonction du modèle d'utilisation de la base de données. Cela ne devrait pas être trop fréquent (car cela gâcherait le réveil du travailleur car il pourrait ne pas y avoir ou trop peu de tuples morts) ou trop retardé (cela causerait beaucoup de tuples morts ensemble et donc un gonflement de la table).
VACUUM ou Full VACUUM
Idéalement, l'application de base de données devrait être conçue de manière à ce qu'il n'y ait pas besoin du FULL VACUUM. Comme expliqué ci-dessus, FULL VACUUM recrée l'espace de stockage et remet les données, donc s'il n'y a que moins de tuples morts, alors immédiatement l'espace de stockage sera recréé pour remettre toutes les données d'origine. De plus, puisque FULL VACUUM prend un verrou exclusif sur la table, il bloque toutes les opérations sur la table correspondante. Donc, faire FULL VACUUM peut parfois ralentir la base de données globale.
En résumé, Full VACUUM doit être évité à moins que l'on sache que la majorité de l'espace de stockage est due à des tuples morts. L'extension PostgreSQL pg_freespacemap peut être utilisée pour obtenir un indice juste sur l'espace libre.
Voyons un exemple du processus VACUUM expliqué.
Commençons par créer une table demo1 :
postgres=# create table demo1(id int, id2 int);
CREATE TABLE
Et insérez-y quelques données :
postgres=# insert into demo1 values(generate_series(1,10000), generate_series(1,
10000));
INSERT 0 10000
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 0.00
(1 row)
Maintenant, supprimons les données :
postgres=# delete from demo1 where id%2=0;
DELETE 5000
Et lancez un aspirateur manuel :
postgres=# vacuum demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 45.07
(1 row)
Cet espace libre est maintenant disponible pour être réutilisé par PostgreSQL, mais si vous souhaitez libérer cet espace pour le système d'exploitation, exécutez :
postgres=# vacuum full demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
23 | 0.00
(1 row)
Conclusion
Et ceci était un court exemple du fonctionnement du processus VACUUM. Heureusement, grâce au processus de vide automatique, la plupart du temps et dans un environnement PostgreSQL commun, vous n'avez pas besoin d'y penser car il est géré par le moteur lui-même.