PostgreSQL vs MongoDB vs Redis : Analyse et Architecture Technique
Toute équipe d’ingénierie fait face un jour au dilemme du choix de la base de données : faut-il standardiser sur un moteur relationnel robuste, adopter une base documentaire flexible ou s’appuyer sur un magasin de structures de données en mémoire ultra-rapide ?
Le débat est souvent résumé de façon réductrice à « Tables vs JSON vs Clé-Valeur ». Cette simplification occulte les fondamentaux techniques qui dictent les performances réelles en production : moteurs de stockage, hiérarchies de mémoire, profils d’E/S disque, primitives de concurrence et mécanismes de journalisation avant écriture (WAL).
Dans les architectures à forte concurrence, un mauvais choix entraîne des coûts d’infrastructure démesurés, une fragmentation de la mémoire, des verrous distribués bloquants ou des pertes de données silencieuses. Ce guide décortique le fonctionnement interne de PostgreSQL, MongoDB et Redis, compare leurs compromis architecturaux et définit un schéma éprouvé de persistance polyglotte.
1. Modèles Mentaux d’Architecture
Avant d’examiner le code ou les bancs d’essai, il convient de comprendre la vocation première et la conception mécanique de chaque moteur :
| Métrique | PostgreSQL | MongoDB | Redis |
|---|---|---|---|
| Paradigme Principal | Relationnel et Objet-Relationnel (SGBDR) | Magasin Documentaire (NoSQL Distribué) | Magasin de Structures de Données en Mémoire |
| Unité de Données | Tuples organisés en pages de 8 Ko | Documents BSON au sein de Collections | Clés en mémoire pointant sur des types typés |
| Moteur de Stockage | Heap + Buffer Pool + WAL | WiredTiger (Arbre B + Journal) | En mémoire (RAM) + Sauvegardes RDB / AOF |
| Modèle de Concurrence | Contrôle de Concurrence Multi-Version (MVCC) | Concurrence au niveau du document + MVCC | Boucle d’événements monothread (Modèle Reactor) |
| Profil de Latence Typique | 1 ms – 15 ms (Disque / Shared Buffers) | 1 ms – 8 ms (Cache WiredTiger / NVMe) | 100 µs – 800 µs (Accès direct à la DRAM) |
| Garantie de Durabilité | ACID strict (Vidage synchrone du WAL) | ACID configurable (Journaling + Write Concern) | Configurable (fsync pour AOF / Instantanés RDB) |
2. Moteurs de Stockage et Gestion de la Mémoire
La façon dont un moteur organise la mémoire vive et transfère les octets sur disque fixe son plafond de débit et sa capacité de reprise après incident.
PostgreSQL : Pages à Fentes, Shared Buffers et Heap
PostgreSQL stocke les données des tables sur disque sous forme de pages fixes de 8 Ko (slotted pages) :
- Buffers Partagés (
shared_buffers) : PostgreSQL gère son propre cache de pages en mémoire afin de ne pas dépendre uniquement des écritures du système d’exploitation. Une lecture vérifie d’abordshared_buffers; en cas d’absence, le noyau charge la page dans le cache du système d’exploitation, puis dans la mémoire de PostgreSQL. - Write-Ahead Logging (WAL) : Lors de la validation d’une transaction (
COMMIT), PostgreSQL enregistre les modifications de manière séquentielle dans le tampon WAL et les synchronise sur disque avant de modifier les pages dans le tas (heap). Ce mécanisme assure la reprise après incident et la réplication physique en continu. - MVCC et Gestion du Bloat : PostgreSQL applique le MVCC en générant une nouvelle version de ligne (
tuple) à chaque mise à jour ou suppression, marquée avecxmin(ID de transaction créatrice) etxmax(ID d’expiration). Les anciens tuples occupant de l’espace dans les pages de 8 Ko, les processus d’arrière-planVACUUMet autovacuum doivent libérer l’espace et figer les identifiants pour éviter le bouclage des transactions (wraparound). - Index Spécialisés : PostgreSQL prend en charge plusieurs types d’index : B-Tree (par défaut), GIN (Index Inversé Généralisé, incontournable pour interroger les champs imbriqués dans les colonnes
JSONB), GiST (géométrie et recherche plein texte) et BRIN (Block Range Index, idéal pour les très grandes tables chronologiques séquentielles).
MongoDB : WiredTiger, BSON et Cache Compressé
MongoDB s’appuie sur le moteur de stockage WiredTiger, remplaçant l’ancienne architecture mmapv1 :
- Allocation Mémoire : Par défaut, WiredTiger alloue
50 % de (RAM - 1 Go)à son cache interne. Le reste de la RAM est réservé au cache du système d’exploitation pour conserver les blocs de disque compressés. - Cache et Compression WiredTiger : Les données sur disque sont compressées avec Snappy (optimisé pour le CPU) ou zlib (densité maximale). WiredTiger décompresse ces blocs en structures BSON dans son cache de travail.
- Points de Contrôle (Checkpoints) et Journal : WiredTiger écrit un instantané de la mémoire sur disque toutes les 60 secondes. Entre deux points de contrôle, les modifications sont consignées dans un journal séquentiel (écrit sur disque toutes les 100 ms par défaut ou immédiatement avec
w: "majority"etj: true). - Contraintes du Modèle Document : Les données sont stockées au format BSON (JSON binaire), gérant nativement les entiers 64 bits, les décimaux et les
ObjectId. Chaque document BSON est limité à 16 Mo pour prévenir la saturation mémoire lors de la lecture d’entités volumineuses.
Redis : Modèle Reactor Monothread, jemalloc et Journaux
Redis adopte une stratégie différente : la mémoire vive est le support de stockage principal, éliminant tout temps d’accès au disque.
- Exécution Monothread : Redis traite les requêtes via une boucle d’événements monothread exploitant le multiplexage d’E/S (
epollsous Linux,kqueuesous macOS/BSD). L’exécution séquentielle sans changement de contexte ni verrou concurrentiel garantit des latences inférieures à la milliseconde. Bien que Redis 6.0 ait introduit des threads auxiliaires pour la gestion réseau des sockets, le traitement des commandes reste strictement monothread. - Allocateur Mémoire : Redis s’appuie sur
jemallocpour limiter la fragmentation. Lorsque la limitemaxmemoryest atteinte, il applique des stratégies d’éviction telles quevolatile-lru,allkeys-lru,volatile-lfuouallkeys-lfu. - Mécanismes de Persistance :
- RDB (Redis Database) : Instantanés binaires ponctuels générés via
bgsave. Redis crée un processus enfant avec Copy-on-Write (COW), exportant la mémoire dans un fichier.rdbsans bloquer les commandes clientes. - AOF (Append-Only File) : Enregistre chaque commande modifiant l’état. La fréquence de synchronisation est configurable :
appendfsync always(durabilité maximale),appendfsync everysec(compromis de référence en production : perte maximale de 1 seconde) ouappendfsync no.
- RDB (Redis Database) : Instantanés binaires ponctuels générés via
3. Modélisation des Données et Ergonomie des Requêtes
Examinons comment chaque système représente une entité métier standard : une Commande contenant un client, un statut et des lignes de produits.
PostgreSQL : Normalisation Relationnelle et JSONB Hybride
PostgreSQL permet de combiner rigueur relationnelle et flexibilité semi-structurée :
-- Approche Relationnelle Normalisée
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES customers(id),
status VARCHAR(32) NOT NULL DEFAULT 'pending',
total_amount NUMERIC(12, 2) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
sku VARCHAR(64) NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price NUMERIC(10, 2) NOT NULL
);
-- Approche Hybride Semistructurée avec JSONB et Index GIN
CREATE TABLE order_events (
id BIGSERIAL PRIMARY KEY,
payload JSONB NOT NULL,
recorded_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_order_events_payload ON order_events USING GIN (payload jsonb_path_ops);
-- Requête sur JSONB avec opérateur de contenance (@>)
SELECT id, payload->>'customer_id' AS customer
FROM order_events
WHERE payload @> '{"status": "paid", "currency": "USD"}';
À retenir : PostgreSQL assure l’intégrité référentielle, la suppression en cascade et la précision des montants avec NUMERIC. En cas de besoin de flexibilité, JSONB offre une structure documentaire indexable tout en maintenant les garanties ACID.
MongoDB : Documents Imbriqués et Pipeline d’Agrégation
Dans MongoDB, les lignes d’articles sont directement imbriquées dans le document racine, supprimant le besoin de jointures :
// Insertion d'un document imbriqué dans MongoDB
db.orders.insertOne({
customerId: ObjectId("66e1f0c29a1b2c3d4e5f6789"),
status: "paid",
totalAmount: 149.50,
items: [
{ sku: "CLAVIER-MECA-01", qty: 1, price: 109.50 },
{ sku: "CABLE-USB-C-2M", qty: 2, price: 20.00 }
],
metadata: { source: "app_mobile", canal: "organique" },
createdAt: new Date()
});
// Requête analytique via le Pipeline d'Agrégation ($match, $unwind, $group)
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $unwind: "$items" },
{ $group: {
_id: "$items.sku",
totalQuantitySold: { $sum: "$items.qty" },
revenue: { $sum: { $multiply: ["$items.qty", "$items.price"] } }
}},
{ $sort: { revenue: -1 } },
{ $limit: 10 }
]);
À retenir : Les documents imbriqués optimisent les lectures complètes. En revanche, les requêtes croisées complexes imposent l’usage du pipeline d’agrégation, et la validation du schéma repose sur l’application ou sur des validateurs $jsonSchema.
Redis : Structures Primitives et Accès Clé-Valeur
Redis ne propose ni tables SQL ni collections libres. Le modèle repose sur des structures primitives typées :
# Enregistrement de la commande dans un Hash pour un accès immédiat
HSET order:1001 customer_id "98234" status "paid" total_amount "149.50"
# Lecture du statut en temps microseconde
HGET order:1001 status
# Ajout à l'historique client via un Sorted Set indexé par horodatage
ZADD customer:98234:orders 1760342400 "order:1001"
# Récupération des 10 commandes les plus récentes du client
ZREVRANGE customer:98234:orders 0 9
# Décrémentation atomique du stock sans concurrence (O(1))
DECRBY inventory:CLAVIER-MECA-01 1
À retenir : Redis demande à l’ingénieur de concevoir manuellement ses index secondaires via des ensembles triés (ZSET), des ensembles (SET) et des tables de hachage (HSET). Il brille sur les accès directs en mémoire.
4. ACID, Transactions et Théorème CAP
La frontière architecturale majeure réside dans la gestion des coupures réseau, des conflits de concurrence et des niveaux d’isolation.
Cohérence (C)
/\
/ \
/ \
/ CA \
/ \
PostgreSQL --/----------\-- MongoDB (Par défaut CP)
/ \
/ \
/________________\
Disponibilité (A) AP Tolérance au Partitionnement (P)
\ /
\-- Redis ---/ (Biais AP en Réplication/Cluster)
PostgreSQL : Conformité ACID Complète et Niveaux ANSI
PostgreSQL propose des garanties transactionnelles strictes sur plusieurs tables :
- Niveaux d’isolation pris en charge : Read Committed (par défaut), Repeatable Read et Serializable.
- Serializable Snapshot Isolation (SSI) : Détecte les anomalies de lecture/écriture sans verrouiller des tables entières, en annulant les transactions qui provoqueraient une anomalie de sérialisation (write skew).
- Intégrité sur instance primaire : La cohérence est absolue sur le nœud maître. Lors de la réplication physique, les répliques fonctionnent en cohérence éventuelle sauf configuration synchrone explicite (
synchronous_commit = on).
MongoDB : Atomicité de Document et Transactions Distribuées 2PC
- Atomicité par Document : Toute mise à jour d’un document individuel est atomique, même sur les sous-documents et tableaux.
- Transactions Multi-Documents : Introduites dans MongoDB 4.0 (sur les replica sets) et 4.2 (sur les clusters shartés), elles s’appuient sur l’isolation par instantané (snapshot isolation) coordonnée par un protocole de validation à deux phases (2PC).
- Cohérence Ajustable : Le compromis CAP s’ajuste au niveau de la requête :
writeConcern: { w: "majority", j: true, wtimeout: 5000 }certifie que l’écriture est validée par le quorum de répliques avant confirmation au client.readConcern: "linearizable"empêche la lecture de données obsolètes lors d’une partition réseau en interrogeant la majorité des membres.
Redis : Atomicité par Boucle d’Événements et Verrouillage Optimiste
- Atomicité par Commande : Comme l’exécution est monothread, les instructions comme
INCR,HSETetLPUSHs’exécutent de façon atomique sans verrous applicatifs. - Blocs
MULTI/EXEC: Regroupent des commandes dans un pipeline exécuté sans entrelacement avec d’autres clients. Attention : Redis ne prend pas en charge le rollback automatique en cas d’erreur de type de données ; les commandes suivantes du bloc sont exécutées. - Verrouillage Optimiste avec
WATCH: Permet des opérations de type Check-And-Set (CAS) :WATCH balance:compte_a # Lecture du solde dans l'application... MULTI DECRBY balance:compte_a 50 INCRBY balance:compte_b 50 EXEC # Retourne nil si balance:compte_a a été modifié par un autre client entre-temps - Scripts Lua et Redis Functions : Permettent d’exécuter une logique transactionnelle complexe directement sur le serveur de façon atomique avec
EVALou les Redis Functions.
5. Latence, Débit et Stratégies de Passage à l’Échelle
Les performances dépendent directement des contraintes matérielles : cache processeur, bande passante de la mémoire vive, IOPS des disques NVMe et latence réseau.
Comparatif de Latence sous Charge
Redis (DRAM) : [0.1ms - 0.8ms] 🚀
MongoDB (WiredTiger): [1.0ms - 5.0ms] ⚡
PostgreSQL (NVMe) : [1.5ms - 12.0ms] 🛡️
- Redis : La latence s’exprime en microsecondes. La recherche en mémoire vive s’effectue en quelques nanosecondes, le temps total étant principalement limité par la couche réseau TCP (0,2 ms – 0,8 ms). Une instance traite couramment entre 100 000 et 500 000 opérations par seconde par cœur.
- MongoDB : Les lectures indexées sur des données chaudes en cache prennent 1 ms à 5 ms. Le débit d’écriture s’accroît horizontalement par partitionnement des données entre shards.
- PostgreSQL : Les recherches par clé primaire sur B-Tree dans les
shared_bufferss’exécutent en 1 ms à 4 ms. Les requêtes complexes avec jointures multiples et fonctions de fenêtrage demandent 10 ms à 100 ms+, traduisant le coût CPU du tri et de la mise en correspondance des lignes.
Stratégies d’Évolutivité
- PostgreSQL :
- Évolutivité verticale : Augmentation du CPU et de la RAM de l’instance principale.
- Répartition des lectures : Répliques physiques en continu (streaming replication).
- Gestion des connexions : L’utilisation d’un gestionnaire de connexions comme PgBouncer ou Odyssey est indispensable pour limiter le coût du modèle multi-processus de PostgreSQL.
- Sharding horizontal : Complexe en natif ; nécessite des extensions spécialisées comme Citus ou un partitionnement au niveau applicatif.
- MongoDB :
- Sharding natif : Conçu nativement pour le passage à l’échelle horizontal via des routeurs de requêtes
mongos, des serveurs de configuration (config servers) et des replica sets de données (shards).
- Sharding natif : Conçu nativement pour le passage à l’échelle horizontal via des routeurs de requêtes
- Redis :
- Redis Cluster : Répartit automatiquement les clés entre 16 384 emplacements de hachage (hash slots).
- Coût mémoire : Limité par le prix de la mémoire vive. Héberger 10 To en RAM dans Redis coûte 15 à 30 fois plus cher que 10 To sur des disques NVMe avec PostgreSQL ou MongoDB.
6. Matrice de Décision pour Architectes
Consultez cette matrice pour aligner votre besoin applicatif sur le moteur adéquat :
| Scénario / Exigence Métier | Moteur Recommandé | Justification Technique Principale |
|---|---|---|
| Comptabilité, Facturation et Transactions | PostgreSQL | Respect strict d’ACID, clés étrangères, précision décimale et tolérance zéro aux pertes de données. |
| Authentification et Cœur Relationnel | PostgreSQL | Modélisation en graphe des droits, intégrité des entités et conformité réglementaire. |
| Catalogues Produits E-Commerce Dynamiques | MongoDB | Attributs hétérogènes sur des millions d’articles sans migrations DDL permanentes. |
| Gestion de Sessions et Révocation de Tokens | Redis | Lecture en microsecondes, expiration automatique par TTL et absence de goulet d’étranglement disque. |
| Limiteurs de Débit Distribués (Rate Limiting) | Redis | Primitives atomiques INCR et EXPIRE ou scripts Lua exécutés sur les passerelles d’API. |
| Classements en Temps Réel (Leaderboards) | Redis | Les Sorted Sets (ZADD, ZRANK) résolvent les classements avec une complexité de $O(\log N)$. |
| Ingestion de Télémétrie IoT (100k écritures/sec) | MongoDB / Redis | Redis pour l’absorption en mémoire tampon ou collections temporelles (time-series) MongoDB avec regroupement automatique. |
| Rapports Analytiques Complexes et Requêtes Ad-hoc | PostgreSQL | Optimiseur SQL complet, fonctions de fenêtrage (ROW_NUMBER(), RANK()), CTE et requêtes parallèles. |
7. Architecture en Production : La Persistance Polyglotte en 3 Niveaux
Les architectures à fort trafic ne s’enferment pas dans une seule base de données. Elles combinent PostgreSQL, MongoDB et Redis au sein d’un modèle de Persistance Polyglotte :
Requête Client
│
▼
┌───────────────────┐
│ Passerelle d'API │
└─────────┬─────────┘
│
1. Rate Limit et Session Client
│
▼
┌─────────────────┐
│ Cache Redis │
│ (Tokens / TTL) │
└────────┬────────┘
│
▼
┌───────────────────┐
│ Backend Principal │
└────┬────────────┬─┘
│ │
2. Transactions / │ │ 3. Catalogue Hétérogène /
Données de Référence│ │ Logs et Événements
▼ ▼
┌────────────┐ ┌────────────┐
│ PostgreSQL │ │ MongoDB │
│ (SSOT) │ │(Catalogue) │
└────────────┘ └────────────┘
Rôle des Composants dans la Pile de Production :
- Redis (Couche d’Accélération et Frontière) :
- Applique la régulation de requêtes via des jetons atomiques (token bucket).
- Gère les sessions utilisateur, les listes de révocation de JWT et la présence WebSocket.
- Sert de cache en lecture (Cache-Aside) pour les résultats de requêtes lourdes PostgreSQL (avec TTL et invalidation par événements).
- PostgreSQL (Source Unique de Vérité - SSOT) :
- Conserve les données critiques : utilisateurs, comptes, factures, abonnements et commandes.
- Enforce l’intégrité référentielle pour éviter qu’une commande ne pointe vers un client inexistant.
- Exploite
JSONBpour stocker les charges utiles variables des webhooks tiers.
- MongoDB (Couche Document Spécialisée) :
- Prend en charge les catalogues de produits polymorphes où chaque catégorie possède des attributs uniques.
- Enregistre les journaux d’audit et les flux d’événements nécessitant un partitionnement horizontal dès le premier jour.
Conclusion et Étapes Suivantes
Il n’existe pas de base de données universelle.
- Faites de PostgreSQL votre socle relationnel par défaut. Sa conformité ACID exemplaire, son écosystème d’extensions (PgBouncer, PostGIS, pgvector) et son support natif de
JSONBrépondent à la majorité des exigences applicatives sans surcoût opérationnel inutile. - Adjoignez Redis à PostgreSQL chaque fois que vous avez besoin de réponses en microsecondes, de verrous distribués ou de gestion d’états éphémères.
- Déployez MongoDB dès lors que vos structures documentaires sont intrinsèquement polymorphes ou que votre volume d’écriture requiert un partitionnement horizontal natif en grappe.
La maîtrise de la modélisation de données, des jointures et de l’optimisation des requêtes constitue le socle indispensable de l’ingénierie moderne.
👉 Perfectionnez votre maîtrise de l’architecture de données sur SQLMaster. Concevez des schémas, manipulez des clés étrangères et exécutez des requêtes avancées directement dans votre navigateur.
Essayez les exemples dans SQL Lab
Copiez la préparation et les requêtes de ce guide dans le laboratoire. Les exemples utilisent SQLite. Les leçons et les consignes du laboratoire sont en anglais.
Ouvrir SQL Lab