SQL vs NoSQL : Modèles de Données, Compromis & Quand Utiliser Quoi
Choisir une base de données ne consiste pas à trouver « la meilleure » — c'est aligner un modèle de données et une garantie de cohérence avec vos patterns d'accès. Ce guide passé en revue chaque grande famille de bases de données, leurs structurés internes et des critères de sélection pratiqués.
Le Clivage Fondamental
| Dimension | SQL (Relationnel) | NoSQL |
|---|---|---|
| Modèle de données | Tables avec lignes et colonnes, schéma strict | Variable : documents, clé-valeur, colonnes larges, graphe |
| Schéma | Schema-on-write (imposé à l'insertion) | Schema-on-read (flexible, souvent sans schéma) |
| Langage de requête | SQL (standardisé) | Variable selon le moteur (certains supportent du SQL-like) |
| Cohérence | Forte (transactions ACID par défaut) | Configurable — d'éventuelle à forte |
| Modèle de scaling | Vertical d'abord, le sharding est complexe | Horizontal d'abord, conçu pour la distribution |
| Jointures | Natives, les jointures multi-tables sont de première classe | Généralement évitées ; la dénormalisation est préférée |
Le théorème CAP stipulé qu'un système distribué peut garantir au maximum deux sur trois : Cohérence, Disponibilité, Tolérance aux partitions. Puisque les partitions réseau sont inévitables, le vrai choix se fait entre cohérence et disponibilité pendant une partition.
Bases de Données Relationnelles (SQL)
Fonctionnement
Les données sont stockées dans des tables (relations) avec un schéma fixé. Chaque ligne possède les mêmes colonnes. Les relations entre tables sont exprimées par des clés étrangères et résolues par des JOINs au moment de la requête.
-- Modèle relationnel normalisé
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(id),
total DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT now()
);
-- Jointure au moment de la requête
SELECT c.name, o.total
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at > '2026-01-01';
Structures internes de stockage
La plupart des bases relationnelles utilisent un index B-tree pour les clés primaires et les index secondaires. Les données sont stockées dans des pages (typiquement 8 Ko dans PostgreSQL, 16 Ko dans MySQL/InnoDB). Le stockage orienté ligne signifie que chaque page contient des lignes complètes — efficace pour l'OLTP, moins pour les scans analytiques.
Write-Ahead Log (WAL) : Toutes les modifications sont d'abord écrites dans un journal séquentiel avant d'être appliquées aux pages de données. Cela garantit la durabilité et la récupération après crash.
Propriétés ACID
| Propriété | Garantie |
|---|---|
| Atomicité | Toutes les opérations d'une transaction réussissent ou aucune |
| Cohérence | Les contraintes (clés étrangères, checks, unicité) sont toujours respectées |
| Isolation | Les transactions concurrentes n'interfèrent pas (niveaux configurables) |
| Durabilité | Les données commitées survivent aux crashs |
Quand choisir SQL
- Relations complexes entre entités (utilisateurs, commandes, produits, factures)
- Cohérence forte non négociable (transactions financières, inventaire)
- Requêtes ad-hoc — l'expressivité de SQL permet aux analystes d'explorer librement
- Écosystème mature — des décennies d'outillage, d'ORMs, de stratégies de sauvegardé
Moteurs principaux
| Moteur | Forces | Managé cloud |
|---|---|---|
| PostgreSQL | Extensions (PostGIS, pgvector, TimescaleDB), JSONB, CTEs | RDS, AlloyDB, Azure, Neon |
| MySQL | Éprouvé à l'échelle web, maturité de la réplication | RDS, Cloud SQL, PlanetScale |
| SQL Server | Fonctionnalités entreprise, intégration .NET | Azure SQL |
| CockroachDB | SQL distribué, sérialisable par défaut | CockroachDB Cloud |
| SQLite | Embarqué, zéro-config, adapté au serverless | Turso (SQLite distribué) |
Bases de Données Document
Fonctionnement
Les données sont stockées sous formé de documents — typiquement du JSON (ou BSON). Chaque document est autonome : il porté toutes ses données sans nécessiter de jointures. Les documents d'une même collection peuvent avoir des structurés différentes.
{
"_id": "order_42",
"customer": {
"name": "Alice Martin",
"email": "alice@example.com"
},
"items": [
{ "sku": "WIDGET-01", "qty": 3, "price": 29.99 },
{ "sku": "GADGET-07", "qty": 1, "price": 149.00 }
],
"total": 238.97,
"status": "shipped",
"created_at": "2026-03-15T10:30:00Z"
}
Structures internes de stockage
MongoDB utilisé un index B-tree sur _id et supporté des index secondaires sur n'importe quel champ, y compris les chemins imbriqués et les tableaux. Les documents sont stockés au format BSON avec le moteur de stockage WiredTiger qui fournit compression et contrôle de concurrence au niveau du document.
Quand choisir les bases document
- Schémas variables — catalogues produits où chaque article a des attributs différents
- Données hiérarchiques/imbriquées — contenu CMS, profils utilisateurs avec préférences embarquées
- Prototypage rapide — pas de migrations, ajoutez des champs librement
- Lecture intensive avec patterns d'accès connus — embarquez les données liées pour éviter les jointures
Quand éviter
- Jointures lourdes entre collections (vous finirez par recréer du SQL en moins bien)
- Transactions touchant de nombreux documents (MongoDB supporté les transactions multi-documents, mais elles sont coûteuses)
Moteurs principaux
| Moteur | Modèle | Notes |
|---|---|---|
| MongoDB | Documents BSON | Leader du marché, Atlas cloud, pipeline d'agrégation |
| Couchbase | Documents JSON | Couche de cache intégrée, langage SQL++ |
| Amazon DocumentDB | Compatible MongoDB | Managé AWS, pas du MongoDB open-source |
| Firestore | Documents JSON | Serverless, synchro temps réel, SDKs mobiles |
| CouchDB | Documents JSON | Réplication multi-maître, offline-first |
Bases Clé-Valeur
Fonctionnement
Le modèle de données le plus simple : une clé correspond à une valeur. Le store est opaque sur la valeur — ce ne sont que des octets. Les recherchés sont en O(1) par clé.
SET session:abc123 '{"user_id": 42, "rôle": "admin"}' EX 3600
GET session:abc123
Structures internes de stockage
Redis stocké tout en mémoire avec une persistance optionnelle (snapshots RDB, journal AOF). Les structurés de données vont au-delà des simples chaînes : listés, ensembles, ensembles triés, hashes, streams et HyperLogLog.
DynamoDB utilisé une clé de partition (hash) pour distribuer les données entre les nœuds et une clé de tri optionnelle pour les requêtes par plage au sein d'une partition. Le stockage est sur SSD avec une latence de l'ordre de la milliseconde.
Quand choisir clé-valeur
- Cache — données de session, réponses API, résultats calculés
- Haut débit, faible latence — paniers d'achat, rate limiting, classements
- Patterns d'accès simples — recherché par ID uniquement, pas de requêtes complexes
Moteurs principaux
| Moteur | Type | Notes |
|---|---|---|
| Redis / Valkey | En mémoire | Structures de données, pub/sub, scripting Lua |
| DynamoDB | Managé, SSD | Auto-scaling, ms unique, tables globales |
| Memcached | En mémoire | Cache pur, multi-thread, plus simple que Redis |
| etcd | KV distribué | Utilisé par Kubernetes, cohérence forte via Raft |
Bases Colonnes Larges (Column-Family)
Fonctionnement
Les données sont organisées en lignes identifiées par une clé de ligne, mais les colonnes sont groupées en familles de colonnes. Contrairement aux tables relationnelles, chaque ligne peut avoir des colonnes différentes, et les colonnes sont stockées ensemble sur disque par famille — permettant des scans efficaces sur des groupes de colonnes spécifiques.
Clé de ligne : user#42
Famille de colonnes "profile" :
name = "Alice"
email = "alice@example.com"
Famille de colonnes "activity" :
last_login = "2026-03-15"
login_count = 142
last_page = "/dashboard"
Structures internes de stockage
Basées sur l'architecture LSM-tree (Log-Structured Merge-tree) :
- Les écritures vont dans une memtable en mémoire
- Quand elle est pleine, elle est vidée sur disque comme une SSTable (Sorted String Table) immuable
- Une compaction en arrière-plan fusionne les SSTables pour réduire l'amplification de lecture
Cela rend les écritures extrêmement rapides (I/O séquentiel) au prix d'une amplification de lecture (peut nécessiter la vérification de plusieurs SSTables).
Quand choisir les colonnes larges
- Données temporelles — lectures de capteurs IoT, métriques, journaux d'événements
- Charges d'écriture lourdes — milliards d'événements par jour
- Lignes larges avec colonnes creuses — suivi d'activité utilisateur
- Distribution géographique — réplication multi-régions
Moteurs principaux
| Moteur | Notes |
|---|---|
| Apache Cassandra | Pair-à-pair, cohérence configurable, langage CQL |
| ScyllaDB | Compatible Cassandra, réécriture C++, latence plus faible |
| HBase | Écosystème Hadoop, cohérence forte, stockage HDFS |
| Google Bigtable | Entièrement managé, propulse Google Search/Maps/Gmail |
| Amazon Keyspaces | Compatible Cassandra managé sur AWS |
Bases de Données Graphe
Fonctionnement
Les données sont modélisées sous formé de nœuds (entités) et d'arêtes (relations). Les deux peuvent porter des propriétés. Les bases graphe excellent dans le parcours de relations — ce qui nécessite des JOINs récursifs coûteux en SQL.
// Cypher (Neo4j) — trouver les amis d'amis qui aiment Python
MATCH (me:Person {name: "Alice"})-[:FRIENDS_WITH]->(friend)-[:FRIENDS_WITH]->(fof)
WHERE (fof)-[:LIKES]->(:Topic {name: "Python"})
AND NOT (me)-[:FRIENDS_WITH]->(fof)
RETURN DISTINCT fof.name
Structures internes de stockage
Stockage graphe natif (Neo4j) : Les nœuds et relations sont stockés comme des enregistrements liés avec des pointeurs physiques directs. Parcourir une relation est un saut de pointeur — O(1) quelle que soit la taille du graphe. C'est l'adjacence sans index.
Graphe non natif (JanusGraph, Amazon Neptune) : Utilise un moteur de stockage sous-jacent (Cassandra, DynamoDB) avec une couche d'index pour les parcours de graphe. Plus scalable horizontalement mais plus lent par saut.
Quand choisir le graphe
- Réseaux sociaux — amis, abonnés, recommandations
- Graphes de connaissances — relations entre entités, ontologies
- Détection de fraude — trouver des patterns relationnels suspects
- Topologie réseau — dépendances d'infrastructure, routage
- Moteurs de recommandation — « les utilisateurs qui ont acheté X ont aussi acheté Y »
Moteurs principaux
| Moteur | Langage de requête | Notes |
|---|---|---|
| Neo4j | Cypher | Leader du marché, stockage graphe natif, ACID |
| Amazon Neptune | Gremlin, SPARQL, openCypher | Managé, graphe de propriétés + RDF |
| ArangoDB | AQL | Multi-modèle (document + graphe + clé-valeur) |
| JanusGraph | Gremlin | Open-source, backend de stockage pluggable |
| Dgraph | GraphQL, DQL | Distribué, support GraphQL natif |
Bases de Données Temporelles (Time-Series)
Fonctionnement
Optimisées pour les données horodatées à ajout intensif où les données récentes sont consultées plus fréquemment. La plupart utilisent un stockage en colonnes avec une compression agressive (encodage delta, encodage par longueur de séquence) car les timestamps et valeurs consécutifs sont souvent similaires.
-- InfluxQL
SELECT mean("cpu_usage")
FROM "server_metrics"
WHERE time > now() - 1h
GROUP BY time(5m), "host"
Quand choisir le time-séries
- Monitoring d'infrastructure — CPU, mémoire, disque, métriques réseau
- IoT — données de capteurs à grande échelle
- Données financières — données tick, chandeliers OHLCV
- Métriques applicatives — latence des requêtes, taux d'erreur, débit
Moteurs principaux
| Moteur | Notes |
|---|---|
| InfluxDB | TSDB dédiée, langage Flux |
| TimescaleDB | Extension PostgreSQL — SQL complet sur du time-séries |
| Prometheus | Métriques pull-based, standard de facto pour Kubernetes |
| Amazon Timestream | TSDB managée et serverless sur AWS |
| ClickHouse | OLAP orienté colonnes qui excelle en time-séries |
| QuestDB | TSDB haute performance avec support SQL |
Bases de Données Vectorielles
Fonctionnement
Stockent des vecteurs d'embedding en haute dimension et supportent la recherché de plus proches voisins approximatifs (ANN). Essentielles pour les applications IA/ML où la recherche par similarité remplacé la correspondance exacte.
# Flux typique de recherche vectorielle
embedding = model.encode("Comment réinitialiser mon mot de passe ?")
results = collection.query(
query_embeddings=[embedding],
n_results=5 # top-5 les plus similaires
)
Structures d'index
| Algorithme | Type | Compromis |
|---|---|---|
| HNSW | Basé graphe | Rappel élevé, plus de mémoire |
| IVF | Fichier inversé | Construction d'index plus rapide, rappel moindre |
| PQ | Quantification produit | Vecteurs compressés, précision moindre |
| ScaNN | Hybride | ANN optimisé par Google |
Moteurs principaux
| Moteur | Notes |
|---|---|
| Pinecone | Managé, serverless, filtrage par métadonnées |
| Weaviate | Open-source, multi-modal, API GraphQL |
| Milvus / Zilliz | Open-source, accéléré GPU, distribué |
| Qdrant | Basé Rust, filtrage + support payload |
| pgvector | Extension PostgreSQL — vecteurs dans votre DB existante |
| ChromaDB | Léger, natif Python, populaire pour le prototypage |
Cadre de Décision
Par pattern d'accès
| Pattern d'Accès | Meilleur Choix |
|---|---|
| Jointures complexes, requêtes ad-hoc, transactions | Relationnel (SQL) |
| Recherche par clé, cache, sessions | Clé-Valeur |
| Objets imbriqués/hiérarchiques, schéma flexible | Document |
| Flux horodatés en append-only | Time-Series |
| Parcours de relations, recherché de chemins | Graphe |
| Écritures volumineuses, lignes larges et creuses | Colonnes Larges |
| Recherche par similarité, embeddings | Vectoriel |
Par exigence de cohérence
| Exigence | Options |
|---|---|
| ACID forte nécessaire | PostgreSQL, MySQL, CockroachDB, Spanner |
| Cohérence configurable OK | Cassandra, DynamoDB, MongoDB |
| Cohérence éventuelle suffisante | Redis (réplication), Couchbase, Cassandra |
La réalité du polyglot persistence
La plupart des systèmes en production utilisent plusieurs bases de données pour différentes charges de travail :
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Redis │ │ Elasticsearch│
│ (commandes, │ │ (sessions, │ │ (recherche │
│ inventaire) │ │ cache) │ │ plein texte)│
└──────┬───────┘ └──────┬──────┘ └──────┬───────┘
│ │ │
└────────────────────┼────────────────────┘
│
┌───────┴───────┐
│ Application │
└───────┬───────┘
│
┌───────┴───────┐
│ Kafka / │
│ Bus d'évts │
└───────┬───────┘
│
┌────────────┼────────────┐
│ │ │
┌──────┴──────┐ ┌──┴───┐ ┌──────┴──────┐
│ ClickHouse │ │ S3 │ │ Neo4j │
│ (analytics) │ │(lac) │ │ (graphe rec)│
└─────────────┘ └──────┘ └─────────────┘
La clé est de laisser les événements circuler entre les systèmes via un bus de messages plutôt que d'essayer de synchroniser les bases directement.
Erreurs Courantes
| Erreur | Impact | Solution |
|---|---|---|
| Utiliser MongoDB pour des données très relationnelles | Jointures manuelles pénibles, duplication de données | Utilisez PostgreSQL avec JSONB pour les parties semi-structurées |
| Utiliser SQL pour des lookups simples par clé à grande échelle | Surcoût inutile | Ajoutez un cache Redis ou passez à DynamoDB |
| Choisir une DB sur la hype | Charge opérationnelle, patterns inadaptés | Alignez la DB sur vos patterns d'accès réels |
| Ignorer les coûts opérationnels | L'open-source « gratuit » nécessite backups, mises à jour, monitoring | Intégrez le coût total managé vs auto-hébergé |
| Dénormalisation prématurée | Incohérence des données, anomalies de mise à jour | Normalisez d'abord, dénormalisez quand vous avez la preuve du besoin |
Ressources
- Designing Data-Intensive Applications — Le livré essentiel de Martin Kleppmann
- Database Internals — Plongée dans les moteurs de stockage par Alex Petrov
- DB-Engines Ranking — Suivi de popularité de 400+ moteurs de bases de données
- Use The Index, Luke — Guide d'indexation et d'optimisation SQL
- Cours CMU Database Group — Cours de systèmes de bases de données d'Andy Pavlo
:::