Aller au contenu principal

Persistance des données

Progression

#Persistance des données

Choisissez un SGBD adapté. Un SQL relationnel convient aux invariants forts et aux requêtes expressives; des bases clé‑valeur ou documents simplifient certains accès à grande échelle mais déplacent des contraintes vers l’application. Écrivez des requêtes lisibles, indexez avec mesure et entourez vos écritures de transactions.

Les index accélèrent les lectures au prix d’écritures plus coûteuses. Un index utile correspond à un prédicat fréquent et sélectif; un index redondant alourdit sans bénéfice. Sur plusieurs colonnes, l’ordre compte. Mesurez avec des plans d’exécution et supprimez les index inactifs.

Les transactions protègent les invariants. Les niveaux d’isolation influencent les anomalies visibles: lecture sale, lecture non répétable, phantom reads. Par défaut, READ COMMITTED suffit souvent; REPEATABLE READ ou SERIALIZABLE se réservent aux zones critiques avec parcimonie et des patterns compatibles.

Mini‑exercice: modélisez users, sessions, posts (SQL) et écrivez les requêtes de base (création, lookup, invalidation de session). Ajoutez un index composite pour accélérer SELECT * FROM posts WHERE author_id=? ORDER BY created_at DESC LIMIT ? et expliquez le choix.

#Animation: de la modélisation au backup

Modéliser
Schéma clair, clés/contrainte
Migrer
Versions atomiques, rollback
Indexer
Égalité → plage → tri
Transactions
ACID, isolation adaptée
Sauvegarder
Backups + restauration testée

#Diagramme: requête dans une transaction

App
DB
1. BEGIN
2. UPDATE/INSERT
3. COMMIT (ou ROLLBACK)

#Diagramme: écriture idempotente (Idempotency-Key)

Client
API
DB
1. POST /payments (Idempotency-Key: k1)
2. SELECT * FROM idem_keys WHERE key=k1
3. MISS → continuer
4. BEGIN + INSERT payment
5. INSERT idem_keys(key, result_hash)
6. COMMIT
7. 201 Created (résultat conservé)
8. RETRY (k1)
9. HIT idem_keys → renvoyer même résultat
10. 201 (réponse répliquée)

#Conseils de persistance

  • Entourez les écritures d’une transaction; choisissez l’isolation la plus faible acceptable.
  • Rendez les handlers idempotents (uniques, upsert, Idempotency-Key) pour supporter les retries.
  • Détectez les deadlocks et rejouez la transaction avec backoff borné.
  • Mesurez avec EXPLAIN et supprimez les index peu utilisés.
  • Automatisez les backups et testez la restauration régulièrement.

#Modélisation SQL: users, sessions, posts

sqlsql
1-- Utilisateurs2CREATE TABLE users (3  id           BIGSERIAL PRIMARY KEY,4  email        TEXT NOT NULL UNIQUE,5  password_hash TEXT NOT NULL,6  created_at   TIMESTAMPTZ NOT NULL DEFAULT now()7);8 9-- Sessions côté serveur10CREATE TABLE sessions (11  id           UUID PRIMARY KEY,                  -- identifiant de session12  user_id      BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,13  created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),14  last_seen_at TIMESTAMPTZ,
Ordre des colonnes d’un index

Règle pratique: égalité → plage → tri. Placez d’abord les colonnes filtrées par égalité, puis celles filtrées par plage, puis l’ordre de tri. Les B‑trees supportent le tri ASC/DESC si spécifié dans l’index.

#Transactions et anomalies d’isolation

sqlsql
1-- Niveau par défaut souvent suffisant2SET TRANSACTION ISOLATION LEVEL READ COMMITTED;3 4-- Zone critique : passer temporairement en REPEATABLE READ/SERIALIZABLE5BEGIN;6SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;7-- ... opérations critiques ...8COMMIT;
  • Lecture non répétable: T1 lit A; T2 modifie A et commit; T1 relit A et observe une valeur différente (READ COMMITTED).
  • Phantom read: T1 lit COUNT(*) avec un prédicat; T2 insère une ligne correspondant au prédicat; T1 relit et observe un nouvel élément (REPEATABLE READ évite non‑répétable mais pas toujours les phantoms selon SGBD).

#Concurrence: éviter la perte de mise à jour

sqlsql
1-- Contrôle d’accès optimiste (OCC) par version2ALTER TABLE posts ADD COLUMN version INT NOT NULL DEFAULT 0;3 4-- Mise à jour sûre5-- application: passe la version lue; si 0 ligne affectée → recharger/relire6UPDATE posts7SET title = $1, body = $2, version = version + 18WHERE id = $id AND version = $current_version;
Idempotence côté API

Combinez UNIQUE/ON CONFLICT DO NOTHING/UPDATE et des Idempotency‑Keys pour rendre vos handlers rejouables sans effets secondaires.

#Idempotency‑Key: schéma minimal

sqlsql
1CREATE TABLE idem_keys (2  key         TEXT PRIMARY KEY,3  result_hash TEXT NOT NULL,4  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()5);6 7-- Garde-fou: purge accélérée par âge8CREATE INDEX idx_idem_gc ON idem_keys (created_at);

#Gestion des deadlocks (retry borné)

tsts
1// Pseudo‑code Node + pg2async function withTxRetry(client, fn, { maxRetries = 3, baseMs = 50 } = {}) {3  for (let attempt = 0; attempt <= maxRetries; attempt++) {4    try {5      await client.query('BEGIN');6      const out = await fn(client);7      await client.query('COMMIT');8      return out;9    } catch (err:any) {10      await client.query('ROLLBACK');11      // Postgres: 40P01 = deadlock detected, 40001 = serialization_failure12      if (['40P01', '40001'].includes(err.code) && attempt < maxRetries) {13        const backoff = baseMs * 2 ** attempt + Math.floor(Math.random() * baseMs);14        await new Promise(r => setTimeout(r, backoff));

#Mesure: lire un plan d’exécution

sqlsql
1EXPLAIN (ANALYZE, BUFFERS)2SELECT id, title3FROM posts4WHERE author_id = $15ORDER BY created_at DESC6LIMIT 20;7/*8  Index Scan using idx_posts_author_created_desc on posts ...9  Filter: (author_id = $1)10  Rows Removed by Filter: 011*/

#Sauvegarde et restauration (PostgreSQL)

bashbash
1# Sauvegarde2pg_dump --format=custom --file=backup.dump "$DATABASE_URL"3# Restauration4pg_restore --clean --if-exists --dbname="$DATABASE_URL" backup.dump

#Quiz rapide

Quel index est le plus adapté à: SELECT * FROM posts WHERE author_id=? ORDER BY created_at DESC LIMIT 20 ?
Quel index est le plus adapté à: SELECT * FROM posts WHERE author_id=? ORDER BY created_at DESC LIMIT 20 ?