Mécanique du moteur transactionnel & MVCC

Module M4

Dalibo SCOP

24.12

18 décembre 2024

Sur ce document

Formation Module M4
Titre Mécanique du moteur transactionnel & MVCC
Révision 24.12
PDF https://dali.bo/m4_pdf
EPUB https://dali.bo/m4_epub
HTML https://dali.bo/m4_html
Slides https://dali.bo/m4_slides
TP https://dali.bo/m4_tp
TP (solutions) https://dali.bo/m4_solutions

Licence Creative Commons CC-BY-NC-SA

Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :

  • Paternité
  • Pas d’utilisation commerciale
  • Partage des conditions initiales à l’identique

Marques déposées

PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.

Versions de PostgreSQL couvertes

Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 13 à 17.

Mécanique du moteur transactionnel & MVCC

PostgreSQL

Introduction

PostgreSQL utilise un modèle appelé MVCC (Multi-Version Concurrency Control).

  • Gestion concurrente des transactions
  • Excellente concurrence
  • Impacts sur l’architecture

Au menu

  • Présentation de MVCC
  • Niveaux d’isolation
  • Implémentation de MVCC de PostgreSQL
  • Les verrous
  • Le mécanisme TOAST

Présentation de MVCC

Définitions

  • MultiVersion Concurrency Control
  • Contrôle de Concurrence Multi-Version
  • Plusieurs versions du même enregistrement
  • Granularité : l’enregistrement (pas le champ !)

Alternative à MVCC : un seul enregistrement en base

  • Verrouillage en lecture et exclusif en écriture
  • Nombre de verrous ?
  • Contention ?
  • Cohérence ?
  • Annulation ?

Implémentation de MVCC par undo

  • MVCC par undo :
    • une version de l’enregistrement dans la table
    • sauvegarde des anciennes versions
    • l’adresse physique d’un enregistrement ne change pas
    • la lecture cohérente est complexe
    • l’undo est complexe à dimensionner… et parfois insuffisant
    • l’annulation est lente
  • Exemple : Oracle

L’implémentation MVCC de PostgreSQL

  • Copy On Write (duplication à l’écriture)
  • Une version d’enregistrement n’est jamais modifiée
  • Toute modification entraîne une nouvelle version
  • Pas d’undo : pas de contention, ROLLBACK instantané

Niveaux d’isolation

Principe des niveaux d’isolation

  • Chaque transaction (et donc session) est isolée à un certain point :
    • elle ne voit pas les opérations des autres
    • elle s’exécute indépendamment des autres
  • Le niveau d’isolation au démarrage d’une transaction peut être spécifié :
    • BEGIN ISOLATION LEVEL xxx;

Niveau READ UNCOMMITTED

  • Non disponible sous PostgreSQL
    • si demandé, s’exécute en READ COMMITTED
  • Lecture de données modifiées par d’autres transactions non validées
  • Aussi appelé dirty reads
  • Dangereux
  • Pas de blocage entre les sessions

Niveau READ COMMITTED

  • Niveau d’isolation par défaut
  • La transaction ne lit que les données validées en base
  • Un ordre SQL s’exécute dans un instantané (les tables semblent figées sur la durée de l’ordre)
  • L’ordre suivant s’exécute dans un instantané différent

Niveau REPEATABLE READ

  • Instantané au début de la transaction
  • Ne voit donc plus les modifications des autres transactions
  • Voit toujours ses propres modifications
  • Peut entrer en conflit avec d’autres transactions si modification des mêmes enregistrements

Niveau SERIALIZABLE

  • Niveau d’isolation le plus élevé
  • Chaque transaction se croit seule sur la base
    • sinon annulation d’une transaction en cours
  • Avantages :
    • pas de « lectures fantômes »
    • évite des verrous, simplifie le développement
  • Inconvénients :
    • pouvoir rejouer les transactions annulées
    • toutes les transactions impliquées doivent être sérialisables

Blocs & lignes

Structure d’un bloc

  • 1 bloc = 8 ko
  • ctid = (bloc, item dans le bloc)
Répartition des lignes au sein d’un bloc (schéma de la documentation officielle, licence PostgreSQL)

xmin & xmax

Table initiale :

xmin xmax Nom Solde
100 M. Durand 1500
100 Mme Martin 2200

xmin & xmax (suite)

BEGIN;
UPDATE soldes SET solde = solde - 200 WHERE nom = 'M. Durand';
xmin xmax Nom Solde
100 150 M. Durand 1500
100 Mme Martin 2200
150 M. Durand 1300

xmin & xmax (suite)

UPDATE soldes SET solde = solde + 200 WHERE nom = 'Mme Martin';
xmin xmax Nom Solde
100 150 M. Durand 1500
100 150 Mme Martin 2200
150 M. Durand 1300
150 Mme Martin 2400

xmin & xmax (suite)

xmin xmax Nom Solde
100 150 M. Durand 1500
100 150 Mme Martin 2200
150 M. Durand 1300
150 Mme Martin 2400
  • Comment est effectuée la suppression d’un enregistrement ?
  • Comment est effectuée l’annulation de la transaction 150 ?

CLOG

  • Le CLOG (Commit Log) enregistre l’état des transactions.
  • Chaque transaction occupe 2 bits de CLOG (4 statuts)
  • COMMIT ou ROLLBACK très rapide
  • Référence pour savoir si une ligne est visible ou pas
  • Puis reporté dans les lignes (hint bits)

Avantages & inconvénients du MVCC de PostgreSQL

Avantages du MVCC de PostgreSQL

  • Avantages classiques de MVCC (concurrence d’accès)
  • Implémentation simple et performante
  • Peu de sources de contention
  • Verrouillage simple d’enregistrement
  • ROLLBACK instantané
  • Données conservées aussi longtemps que nécessaire

Inconvénients du MVCC de PostgreSQL

  • Nettoyage des enregistrements
    • VACUUM
    • automatisation : autovacuum
  • Tables plus volumineuses
  • Écritures amplifiées
  • Pas de visibilité des lignes dans les index
  • Les colonnes supprimées impliquent reconstruction

Le wraparound

Le wraparound (1)

  • Wraparound : bouclage du compteur de xmin/xmax
  • 32 bits ~ 4 milliards

Le wraparound (2)

Après 4 milliards de transactions :

Le wraparound (3)

Concrètement ?

  • VACUUM FREEZE
    • géré par l’autovacuum
    • au pire, d’office
    • potentiellement beaucoup d’écritures

Optimisations de MVCC

  • HOT
  • Free Space Map
  • Visibility Map

Mise à jour jour HOT

HOT = Heap-Only Tuples

  • Si place dans le bloc
  • Si aucune colonne indexée modifiée
  • Alors la mise à jour se fait dans le même bloc
    • gain en mise à jour des index
    • gain en écritures et en utilisation du cache
  • Favorisée par un fillfator < 100

Free Space Map

  • Fichier pointant les espaces libres des blocs
  • Optimisation des insertions

Visibility Map

  • Quels blocs sont intégralement visibles ?
  • Mise à jour par VACUUM
  • Utilisation :
    • Accélérer VACUUM et VACUUM FREEZE
    • Index Only Scan

Verrous

Verrouillage et MVCC

La gestion des verrous est liée à l’implémentation de MVCC

  • Verrouillage d’objets en mémoire
  • Verrouillage d’objets sur disque
  • Paramètres

Le gestionnaire de verrous

PostgreSQL possède un gestionnaire de verrous

  • Verrous d’objet
  • Niveaux de verrouillage
  • Empilement des verrous
  • Deadlock
  • Vue pg_locks

Verrous sur enregistrement

  • Le gestionnaire de verrous possèdes des verrous sur enregistrements
    • transitoires
    • le temps de poser le xmax
  • Utilisation de verrous sur disque
    • pas de risque de pénurie
  • Les verrous entre transaction se font sur leurs ID

La vue pg_locks

  • pg_locks :
    • visualisation des verrous en place
    • tous types de verrous sur objets
  • Complexe à interpréter :
    • verrous sur enregistrements pas directement visibles

Verrous - Paramètres

  • Nombre :
    • max_locks_per_transaction (+ paramètres pour la sérialisation)
  • Durée :
    • lock_timeout (éviter l’empilement des verrous)
    • deadlock_timeout (défaut 1 s)
  • Trace :
    • log_lock_waits

Durées de sessions, transactions & ordres

Quelle durée pour les sessions & transactions ?

Divers seuils possibles, jamais globalement.

SET …_timeout TO '5s' ;
ALTER ROLEIN DATABASESET ..._timeout TO '…s'
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

Quelle durée pour une session ?

  • Courte
    • coût & temps des connexions
    • pooler ?
  • Longue
    • risque de saturation du nombre de connexions
    • (rare) gaspillage mémoire par les backends

Quelle durée pour une transaction ?

  • Courte
    • synchronisation fréquente coûteuse
  • Longue
    • verrous bloquants

TOAST

Mécanisme TOAST

TOAST : The Oversized-Attribute Storage Technique

Que faire si une ligne dépasse d’un bloc ?

  • Compresser
  • Déporter dans une table
  • Ou les deux
  • Inutile de le faire dans l’applicatif
  • Politique par champ
    • PLAIN/MAIN/EXTERNAL/EXTENDED

TOAST & table de débordement

  • Table de débordement pg_toast_XXX
    • masquée, transparente
  • Jusqu’à 1 Go par champ (déconseillé)
    • texte, JSON, binaire…
    • compression optionnelle
  • Une raison de plus d’éviter les SELECT *

TOAST & compression

  • pglz (zlib) : défaut
  • lz4 à préférer
    • généralement plus rapide
    • compression équivalente (à vérifier)
  • Mise en place :
default_toast_compression = lz4

ou :

ALTER TABLE t1 ALTER COLUMN c2 SET COMPRESSION lz4 ;

Conclusion

  • PostgreSQL dispose d’une implémentation MVCC complète, permettant :
    • que les lecteurs ne bloquent pas les écrivains
    • que les écrivains ne bloquent pas les lecteurs
    • que les verrous en mémoire soient d’un nombre limité
  • Cela impose par contre une mécanique un peu complexe, dont les parties visibles sont la commande VACUUM et le processus d’arrière-plan autovacuum.

Questions

N’hésitez pas, c’est le moment !

Quiz

Travaux pratiques

Niveaux d’isolation READ COMMITTED et REPEATABLE READ

Niveau d’isolation SERIALIZABLE (Optionnel)

Effets de MVCC

Verrous

Travaux pratiques (solutions)