Stockage de données évolutif : SQL ou NoSQL
Choisissez entre bases de données relationnelles, magasins clé-valeur, bases documentaires et magasins en colonnes larges selon les modes d’accès, la cohérence et les exigences de montée en charge.
Stockage de données évolutif : SQL ou NoSQL est une leçon DSA Interview Prep gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage DSA Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours DSA Interview Prep comprend 4 leçons au total.
Le choix du stockage est un compromis
Choisir un magasin de données est l’une des décisions les plus lourdes de conséquences dans la conception de systèmes. Aucune base de données n’est universellement meilleure — chaque type privilégie différents modes d’accès, garanties de cohérence et caractéristiques de montée en charge. Un mauvais choix en production peut entraîner des mois de migration pénible.
Lors des entretiens, les examinateurs cherchent à vérifier que vous comprenez les différences fondamentales et que vous savez associer un moteur de stockage aux exigences d’un problème. La question n’est jamais « lequel est meilleur ? », mais « lequel est meilleur pour cette charge de travail précise ? » Justifiez toujours votre choix en vous appuyant sur des exigences précises.
# Storage decision matrix summary
factors = [
'Data structure (tabular, documents, key-value, graph, time-series)',
'Read vs write ratio (read-heavy, write-heavy, balanced)',
'Query patterns (point lookups, range scans, aggregations, joins)',
'Consistency requirements (ACID vs eventual consistency)',
'Scale requirements (single node, sharding, global distribution)',
'Latency requirements (milliseconds vs microseconds)',
'Team familiarity and operational complexity',
]
print('Key factors for storage selection:')
for f in factors:
print(f' - {f}')Bases de données relationnelles (SQL) : atouts
Les bases de données relationnelles (PostgreSQL, MySQL, SQLite) stockent les données dans des tables à schémas fixes et prennent en charge les transactions ACID — atomicité, cohérence, isolation et durabilité. Elles excellent dans les requêtes complexes utilisant des jointures, des agrégations et des filtres, ce qui les rend idéales pour les données structurées dont les relations sont bien définies.
Principaux atouts : requêtes complexes sur plusieurs tables grâce à SQL, contraintes de clés étrangères garantissant l’intégrité des données, indexation puissante (arbre B, hachage, texte intégral), écosystème mature avec réplication et sauvegardes. Utilisez SQL lorsque vos données sont fortement relationnelles, que la cohérence est essentielle et que les requêtes sont complexes et variées.
# SQL excels at: complex queries, joins, transactions
# Example: find top 5 products by revenue this month
sql_query = '''
SELECT p.name, SUM(oi.quantity * oi.price) AS revenue
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= DATE_TRUNC('month', NOW())
GROUP BY p.id, p.name
ORDER BY revenue DESC
LIMIT 5;
'''
print('SQL shines for relational queries with JOINs:')
print(sql_query)
print('ACID guarantees example (transfer $100 between accounts):')
transfer_sql = '''
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- either both succeed or neither does
'''
print(transfer_sql)Difficultés de mise à l’échelle de SQL
Les bases de données SQL se mettent naturellement à l’échelle verticalement (serveur plus puissant, davantage de CPU/RAM), mais la mise à l’échelle horizontale est complexe. Les répliques de lecture prennent en charge les charges de travail comportant beaucoup de lectures en acheminant les lectures vers les nœuds répliqués et les écritures vers le nœud primaire. Cependant, le débit d’écriture reste limité à un seul nœud primaire, sauf si vous ajoutez un partitionnement horizontal.
Le partitionnement horizontal répartit les données entre plusieurs instances de base de données à l’aide d’une clé de partitionnement (par exemple, l’identifiant utilisateur modulo N). Cela permet d’augmenter le débit d’écriture, mais empêche les jointures et les transactions entre partitions — deux des atouts majeurs de SQL. La plupart des applications web dépassent les capacités d’une base SQL sur un seul nœud autour de 5 à 10 TB de données ou d’environ 100 000 écritures par seconde.
# SQL scaling strategies
strategies = {
'Read replicas': {
'how': 'One primary (writes), multiple replicas (reads)',
'scales': 'Read throughput (10x+)',
'limit': 'Write throughput still bounded by single primary',
},
'Connection pooling (PgBouncer)': {
'how': 'Pool of persistent DB connections shared among app servers',
'scales': 'Connection count (PostgreSQL max ~500 connections)',
'limit': 'Does not increase query throughput',
},
'Horizontal sharding': {
'how': 'Partition rows by shard key across N database instances',
'scales': 'Both reads and writes (N×)',
'limit': 'Cross-shard joins and transactions broken; complex routing',
},
'CQRS': {
'how': 'Separate write model (SQL) from read model (denormalised/NoSQL)',
'scales': 'Optimise each path independently',
'limit': 'Eventual consistency between write and read models',
},
}
for strategy, info in strategies.items():
print(f'{strategy}:\n How: {info["how"]}\n Scales: {info["scales"]}\n Limit: {info["limit"]}\n')Bases clé-valeur : Redis et DynamoDB
Les bases clé-valeur stockent les données sous forme de paires clé → valeur, avec des lectures et des écritures en O(1) sur la clé. Elles renoncent à la souplesse des requêtes au profit de performances extrêmes et d’une grande évolutivité horizontale. Redis, qui fonctionne en mémoire, atteint une latence de quelques microsecondes ; DynamoDB, qui est géré automatiquement, atteint une latence de quelques millisecondes avec une mise à l’échelle automatique, quel que soit le débit.
Utilisez les bases clé-valeur pour : le stockage des sessions, la mise en cache, les indicateurs de fonctionnalité, les associations d’un raccourcisseur d’URL, les paniers d’achat et les classements en temps réel. Ne les utilisez pas lorsque vous avez besoin de requêtes complexes, de relations entre entités ou de filtrage ponctuel — vous ne pouvez effectuer une recherche que par clé exacte.
# Key-value store use cases
kv_operations = {
'GET key': 'O(1) point lookup — the core operation',
'SET key value': 'O(1) insert or update',
'DEL key': 'O(1) delete',
'EXPIRE key ttl': 'Set time-to-live; key auto-deleted after ttl seconds',
'INCR key': 'Atomic increment — useful for counters and rate limiting',
'LPUSH/LRANGE': 'List operations — useful for queues and recent-items feeds',
'ZADD/ZRANGE': 'Sorted set — leaderboards, rate limiting with sliding window',
}
print('Redis operation set:')
for op, desc in kv_operations.items():
print(f' {op:25s}: {desc}')
print('\nDynamoDB vs Redis:')
print(' Redis: microsecond latency, in-memory, needs persistence config')
print(' DynamoDB: single-digit ms, managed, auto-scaling, durable by default')Bases orientées documents : MongoDB
Les bases orientées documents (MongoDB, Couchbase) stockent les données sous forme de documents semblables à JSON, ce qui permet d’utiliser des schémas flexibles — les champs peuvent différer d’un document à l’autre au sein d’une même collection. Elles prennent en charge l’indexation sur n’importe quel champ ainsi que des requêtes relativement riches (filtres, projections, agrégations), même si les transactions portant sur plusieurs documents sont plus limitées qu’avec SQL.
Les bases orientées documents conviennent aux applications dans lesquelles : la structure des données varie selon l’entité (profils utilisateur avec des attributs différents), une évolution rapide du schéma est attendue (jeunes entreprises qui modifient fréquemment leurs modèles de données) ou les modes de lecture consistent surtout à récupérer des entités complètes plutôt qu’à effectuer des jointures entre tables.
# MongoDB document example
user_doc = {
'_id': 'user123',
'name': 'Alice',
'email': 'alice@example.com',
'preferences': {
'theme': 'dark',
'language': 'en',
'notifications': ['email', 'push']
},
'addresses': [
{'type': 'home', 'city': 'Berlin', 'country': 'DE'},
{'type': 'work', 'city': 'Munich', 'country': 'DE'}
],
'subscription_tier': 'pro',
# Note: not all users have all fields -- flexible schema!
}
import json
print('Document structure (flexible schema):')
print(json.dumps(user_doc, indent=2))
print('\nDocument store strengths:')
print(' - Nested/array fields without joins')
print(' - Flexible schema (different fields per document)')
print(' - Scales horizontally by sharding on _id')Bases en colonnes larges : Cassandra
Les bases en colonnes larges (Apache Cassandra, HBase) stockent les données dans des lignes et des colonnes, mais permettent à chaque ligne de posséder un ensemble de colonnes différent. Elles sont conçues pour un débit d’écriture massif réparti entre de nombreux nœuds, avec une cohérence éventuelle par défaut. Cassandra offre une mise à l’échelle linéaire des écritures : doubler le nombre de nœuds double le débit d’écriture.
Le compromis est le suivant : les requêtes doivent être conçues autour de la clé de partitionnement. Vous ne pouvez pas filtrer ni trier efficacement sur des colonnes arbitraires — vous devez d’abord définir le mode de requête, puis concevoir la table pour y répondre. C’est l’opposé de l’approche de SQL : « concevoir les données, puis écrire n’importe quelle requête ».
# Cassandra table design for time-series events
# Design around the query: 'give me all events for user X, most recent first'
cassandra_table = '''
CREATE TABLE user_events (
user_id UUID,
event_time TIMESTAMP,
event_type TEXT,
metadata MAP<TEXT, TEXT>,
PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);
-- Query (matches partition key exactly):
SELECT * FROM user_events WHERE user_id = ? LIMIT 100;
'''
print('Cassandra wide-column design:')
print(cassandra_table)
print('Properties:')
print(' - user_id = partition key (all rows for one user on same node)')
print(' - event_time = clustering key (sorted within partition)')
print(' - Very fast writes: append-only, no locking')
print(' - Cannot query by event_type alone (no partition key)')Théorème CAP : cohérence, disponibilité, tolérance au partitionnement
Le théorème CAP affirme qu’un système distribué peut garantir au maximum deux des propriétés suivantes : Cohérence (chaque lecture renvoie la dernière écriture), Adisponibilité (chaque requête reçoit une réponse qui ne signale pas d’erreur) et Ptolérance au partitionnement (le système continue à fonctionner malgré les partitions réseau).
Comme les partitions réseau surviennent toujours dans les systèmes distribués, le véritable choix se fait entre CP (privilégier la cohérence, au risque de refuser des requêtes pendant une partition) et AP (privilégier la disponibilité, au risque de renvoyer des données obsolètes). Les bases de données SQL sont généralement CP ; Cassandra et DynamoDB sont AP (avec une cohérence éventuelle par défaut). Redis Cluster est CP.
# CAP theorem applied to common databases
databases = {
'PostgreSQL (single node)': {'C': True, 'A': True, 'P': False, 'note': 'Not distributed; CA'},
'PostgreSQL (multi-AZ)': {'C': True, 'A': False, 'P': True, 'note': 'CP: primary fails over, brief downtime'},
'MySQL Cluster': {'C': True, 'A': False, 'P': True, 'note': 'CP'},
'Cassandra': {'C': False, 'A': True, 'P': True, 'note': 'AP: eventual consistency default'},
'DynamoDB (default)': {'C': False, 'A': True, 'P': True, 'note': 'AP: eventual consistency'},
'DynamoDB (strong read)': {'C': True, 'A': False, 'P': True, 'note': 'CP: strongly consistent reads'},
'Redis Cluster': {'C': True, 'A': False, 'P': True, 'note': 'CP'},
'MongoDB (default)': {'C': True, 'A': False, 'P': True, 'note': 'CP: reads from primary'},
}
for db, caps in databases.items():
c_str = 'C' if caps['C'] else '-'
a_str = 'A' if caps['A'] else '-'
p_str = 'P' if caps['P'] else '-'
print(f'{db:35s} [{c_str}{a_str}{p_str}] {caps["note"]}')Choisir le stockage : un cadre de décision
Voici un cadre pratique pour choisir une solution de stockage lors des entretiens :
- Besoin de transactions ACID ? → Base relationnelle (PostgreSQL, MySQL)
- Besoin de recherches en moins d’une milliseconde ou de mise en cache ? → Base clé-valeur (Redis)
- Besoin d’un schéma flexible ou de données orientées documents ? → Base orientée documents (MongoDB)
- Besoin d’un débit d’écriture massif (>100 000/s) avec des données de séries temporelles ou d’événements ? → Base en colonnes larges (Cassandra)
- Besoin d’une distribution mondiale avec une mise à l’échelle gérée ? → DynamoDB ou Cosmos DB
- Besoin de parcours dans un graphe ? → Base de données orientée graphe (Neo4j)
# Decision tree in code form
def choose_storage(needs_acid, high_write_throughput, flexible_schema,
sub_ms_latency, graph_queries, global_scale):
if sub_ms_latency:
return 'Redis (in-memory key-value)'
if graph_queries:
return 'Neo4j (graph database)'
if needs_acid:
return 'PostgreSQL / MySQL (relational)'
if high_write_throughput and not flexible_schema:
return 'Cassandra (wide-column, write-optimised)'
if flexible_schema:
return 'MongoDB (document store)'
if global_scale:
return 'DynamoDB or Cosmos DB (managed global KV/document)'
return 'PostgreSQL (safe default for most web apps)'
# Example scenarios
scenarios = [
{'needs_acid': True, 'high_write_throughput': False, 'flexible_schema': False,
'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
{'needs_acid': False, 'high_write_throughput': True, 'flexible_schema': False,
'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
{'needs_acid': False, 'high_write_throughput': False, 'flexible_schema': False,
'sub_ms_latency': True, 'graph_queries': False, 'global_scale': False},
]
for s in scenarios:
print(f'{choose_storage(**s)}')Persistance polyglotte : utiliser plusieurs bases
Les systèmes de production utilisent rarement une seule base de données pour tout. La persistance polyglotte consiste à utiliser la technologie de stockage adaptée à chaque partie du système. Une application web classique peut utiliser : PostgreSQL pour les données de référence des utilisateurs et des commandes, Redis pour le stockage des sessions et la mise en cache, Elasticsearch pour la recherche en texte intégral, S3 pour le stockage de fichiers, et Cassandra pour les journaux d’événements et les analyses.
Le compromis est l’augmentation de la complexité opérationnelle avec chaque type de base de données. L’équipe doit maintenir, surveiller et sauvegarder plusieurs systèmes. Justification de conception : à grande échelle, les gains de performance et d’évolutivité obtenus en associant chaque charge de travail au stockage adapté compensent le coût d’exploitation.
# Polyglot persistence in an e-commerce system
components = {
'User accounts, orders, payments': {
'storage': 'PostgreSQL',
'reason': 'ACID transactions (payment integrity), complex queries',
},
'Product catalogue': {
'storage': 'MongoDB or PostgreSQL with JSONB',
'reason': 'Flexible product attributes vary by category',
},
'Session tokens': {
'storage': 'Redis (with TTL)',
'reason': 'O(1) lookup, automatic expiry, high throughput',
},
'Product search': {
'storage': 'Elasticsearch',
'reason': 'Full-text search, faceted filtering, relevance scoring',
},
'Activity/event log': {
'storage': 'Cassandra or Kafka + S3',
'reason': 'High write throughput, append-only, time-series queries',
},
'Product images / videos': {
'storage': 'S3 + CloudFront CDN',
'reason': 'Cheap object storage, global distribution via CDN',
},
}
for component, info in components.items():
print(f'{component}:\n {info["storage"]}: {info["reason"]}\n')Stratégie d’indexation selon les types de stockage
Tous les systèmes de stockage utilisent des index pour accélérer les lectures, au prix d’écritures plus lentes et d’un espace de stockage supplémentaire. Comprendre l’indexation selon les types de stockage est essentiel pour la conception de systèmes :
- SQL : index en arbre B sur n’importe quelle colonne ; index composites pour les requêtes portant sur plusieurs colonnes ; index couvrants pour éviter les consultations de table
- MongoDB : index sur n’importe quel champ ; index composés ; index TTL pour l’expiration automatique
- Cassandra : seules la clé de partitionnement et les colonnes de regroupement sont indexées nativement ; les index secondaires sont coûteux
- Redis : ensembles triés utilisés comme index pour les requêtes par intervalle ; aucune indexation traditionnelle nécessaire pour les bases clé-valeur
# Indexing examples across storage types
# PostgreSQL: B-tree composite index
postgres_index = '''
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
-- Optimises: SELECT * FROM orders WHERE user_id=? ORDER BY created_at DESC
'''
# MongoDB: compound index
mongo_index = '''
db.products.createIndex({ category: 1, price: -1 })
// Optimises: db.products.find({category:'Electronics'}).sort({price:-1})
'''
# Cassandra: cluster key ordering (built into table design)
cassandra_index = '''
-- No separate index needed; clustering key IS the index:
PRIMARY KEY (user_id, event_time) WITH CLUSTERING ORDER BY (event_time DESC)
'''
print('PostgreSQL:', postgres_index)
print('MongoDB:', mongo_index)
print('Cassandra:', cassandra_index)SQL ou NoSQL lors des entretiens : que dire
Lorsqu’on vous demande « SQL ou NoSQL ? » pendant un entretien de conception de systèmes, ne répondez jamais en un seul mot. Suivez plutôt cette structure :
- Présentez la charge de travail : « Il s’agit d’une charge comportant beaucoup de lectures et un filtrage complexe, donc… »
- Présentez l’exigence : « Nous avons besoin d’une forte cohérence pour les transactions financières, donc… »
- Présentez le choix : « J’utiliserais PostgreSQL avec des répliques de lecture »
- Présentez le compromis : « Le compromis est que la mise à l’échelle horizontale des écritures nécessite un partitionnement horizontal, ce qui ajoute de la complexité »
- Mentionnez une autre possibilité : « Si le volume d’écritures était plus élevé, nous pourrions envisager DynamoDB »
# Sample answer structure for 'SQL or NoSQL?'
def answer_storage_question(workload, consistency_need, scale):
print(f'Workload: {workload}')
print(f'Consistency: {consistency_need}')
print(f'Scale: {scale}')
print()
if 'financial' in workload.lower() or consistency_need == 'strong':
choice = 'PostgreSQL (ACID, strong consistency)'
tradeoff = 'Horizontal write scaling requires sharding'
alternative = 'Google Spanner for global transactions'
elif 'event' in workload.lower() or 'log' in workload.lower():
choice = 'Cassandra (high write throughput, time-series)'
tradeoff = 'Eventual consistency; queries limited to partition key'
alternative = 'Kafka + S3 for long-term event archival'
else:
choice = 'DynamoDB (managed, auto-scale, low latency)'
tradeoff = 'Limited query flexibility; cross-item transactions limited'
alternative = 'PostgreSQL if complex queries emerge'
print(f'Choice: {choice}\nTrade-off: {tradeoff}\nAlternative: {alternative}')
answer_storage_question('Social media feed', 'eventual', '10M users')Vérification rapide
Testez votre compréhension des concepts de structures de données et d’algorithmes — préparation aux entretiens de programmation abordés dans cette leçon.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris : les bases de données SQL fournissent des transactions ACID et des requêtes complexes, mais la mise à l’échelle des écritures est difficile, tandis que les bases de données NoSQL renoncent à la cohérence ou à la souplesse des requêtes au profit d’une évolutivité et d’une disponibilité extrêmes, le théorème CAP oblige les systèmes distribués à choisir entre cohérence et disponibilité pendant les partitions réseau, et les systèmes de production utilisent généralement une persistance polyglotte — chaque charge de travail étant associée au moteur de stockage adapté. Ensuite, nous verrons les couches de mise en cache, les CDN et l’équilibrage de charge pour faire évoluer davantage les systèmes comportant beaucoup de lectures.
Questions Fréquemment Posées
La leçon « Stockage de données évolutif : SQL ou NoSQL » est-elle gratuite ?
Oui — le texte complet de « Stockage de données évolutif : SQL ou NoSQL » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours DSA Interview Prep, passe à CoddyKit PRO. Le cours DSA Interview Prep comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Stockage de données évolutif : SQL ou NoSQL » ?
Choisissez entre bases de données relationnelles, magasins clé-valeur, bases documentaires et magasins en colonnes larges selon les modes d’accès, la cohérence et les exigences de montée en charge. Tu pratiques DSA Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer DSA Interview Prep ?
Aucune expérience préalable n'est requise. DSA Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Stockage de données évolutif : SQL ou NoSQL » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon DSA Interview Prep ?
Oui. Chaque leçon DSA Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Cadre d’entretien de conception de systèmes
- Stockage de données évolutif : SQL ou NoSQL
- Mise en cache, CDN et équilibrage de charge
- Concevoir un limiteur de débit et un fil Twitter