tadata
Retour à l'accueil

SQL vs NoSQL : Modèles de Données, Compromis & Quand Utiliser Quoi

#databases#sql#nosql#data-engineering#cloud

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

DimensionSQL (Relationnel)NoSQL
Modèle de donnéesTables avec lignes et colonnes, schéma strictVariable : documents, clé-valeur, colonnes larges, graphe
SchémaSchema-on-write (imposé à l'insertion)Schema-on-read (flexible, souvent sans schéma)
Langage de requêteSQL (standardisé)Variable selon le moteur (certains supportent du SQL-like)
CohérenceForte (transactions ACID par défaut)Configurable — d'éventuelle à forte
Modèle de scalingVertical d'abord, le sharding est complexeHorizontal d'abord, conçu pour la distribution
JointuresNatives, les jointures multi-tables sont de première classeGé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érenceLes contraintes (clés étrangères, checks, unicité) sont toujours respectées
IsolationLes 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

MoteurForcesManagé cloud
PostgreSQLExtensions (PostGIS, pgvector, TimescaleDB), JSONB, CTEsRDS, AlloyDB, Azure, Neon
MySQLÉprouvé à l'échelle web, maturité de la réplicationRDS, Cloud SQL, PlanetScale
SQL ServerFonctionnalités entreprise, intégration .NETAzure SQL
CockroachDBSQL distribué, sérialisable par défautCockroachDB Cloud
SQLiteEmbarqué, zéro-config, adapté au serverlessTurso (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

MoteurModèleNotes
MongoDBDocuments BSONLeader du marché, Atlas cloud, pipeline d'agrégation
CouchbaseDocuments JSONCouche de cache intégrée, langage SQL++
Amazon DocumentDBCompatible MongoDBManagé AWS, pas du MongoDB open-source
FirestoreDocuments JSONServerless, synchro temps réel, SDKs mobiles
CouchDBDocuments JSONRé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

MoteurTypeNotes
Redis / ValkeyEn mémoireStructures de données, pub/sub, scripting Lua
DynamoDBManagé, SSDAuto-scaling, ms unique, tables globales
MemcachedEn mémoireCache pur, multi-thread, plus simple que Redis
etcdKV 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) :

  1. Les écritures vont dans une memtable en mémoire
  2. Quand elle est pleine, elle est vidée sur disque comme une SSTable (Sorted String Table) immuable
  3. 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

MoteurNotes
Apache CassandraPair-à-pair, cohérence configurable, langage CQL
ScyllaDBCompatible Cassandra, réécriture C++, latence plus faible
HBaseÉcosystème Hadoop, cohérence forte, stockage HDFS
Google BigtableEntièrement managé, propulse Google Search/Maps/Gmail
Amazon KeyspacesCompatible 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

MoteurLangage de requêteNotes
Neo4jCypherLeader du marché, stockage graphe natif, ACID
Amazon NeptuneGremlin, SPARQL, openCypherManagé, graphe de propriétés + RDF
ArangoDBAQLMulti-modèle (document + graphe + clé-valeur)
JanusGraphGremlinOpen-source, backend de stockage pluggable
DgraphGraphQL, DQLDistribué, 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

MoteurNotes
InfluxDBTSDB dédiée, langage Flux
TimescaleDBExtension PostgreSQL — SQL complet sur du time-séries
PrometheusMétriques pull-based, standard de facto pour Kubernetes
Amazon TimestreamTSDB managée et serverless sur AWS
ClickHouseOLAP orienté colonnes qui excelle en time-séries
QuestDBTSDB 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

AlgorithmeTypeCompromis
HNSWBasé grapheRappel élevé, plus de mémoire
IVFFichier inverséConstruction d'index plus rapide, rappel moindre
PQQuantification produitVecteurs compressés, précision moindre
ScaNNHybrideANN optimisé par Google

Moteurs principaux

MoteurNotes
PineconeManagé, serverless, filtrage par métadonnées
WeaviateOpen-source, multi-modal, API GraphQL
Milvus / ZillizOpen-source, accéléré GPU, distribué
QdrantBasé Rust, filtrage + support payload
pgvectorExtension PostgreSQL — vecteurs dans votre DB existante
ChromaDBLéger, natif Python, populaire pour le prototypage

Cadre de Décision

Par pattern d'accès

Pattern d'AccèsMeilleur Choix
Jointures complexes, requêtes ad-hoc, transactionsRelationnel (SQL)
Recherche par clé, cache, sessionsClé-Valeur
Objets imbriqués/hiérarchiques, schéma flexibleDocument
Flux horodatés en append-onlyTime-Series
Parcours de relations, recherché de cheminsGraphe
Écritures volumineuses, lignes larges et creusesColonnes Larges
Recherche par similarité, embeddingsVectoriel

Par exigence de cohérence

ExigenceOptions
ACID forte nécessairePostgreSQL, MySQL, CockroachDB, Spanner
Cohérence configurable OKCassandra, DynamoDB, MongoDB
Cohérence éventuelle suffisanteRedis (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

ErreurImpactSolution
Utiliser MongoDB pour des données très relationnellesJointures manuelles pénibles, duplication de donnéesUtilisez PostgreSQL avec JSONB pour les parties semi-structurées
Utiliser SQL pour des lookups simples par clé à grande échelleSurcoût inutileAjoutez un cache Redis ou passez à DynamoDB
Choisir une DB sur la hypeCharge opérationnelle, patterns inadaptésAlignez la DB sur vos patterns d'accès réels
Ignorer les coûts opérationnelsL'open-source « gratuit » nécessite backups, mises à jour, monitoringIntégrez le coût total managé vs auto-hébergé
Dénormalisation prématuréeIncohérence des données, anomalies de mise à jourNormalisez d'abord, dénormalisez quand vous avez la preuve du besoin

Ressources

:::

Nous utilisons des cookies analytiques pour améliorer votre expérience. Aucune donnée personnelle n'est collectée.