Persistance distribuée¶
Choisir les bonnes bases, comprendre les trade-offs de consistency et partitionner les données à l'échelle.
Polyglot persistence¶
Chaque sous-domaine utilise la base la mieux adaptée a ses besoins. Un catalogue produits avec des attributs variables n'a pas les mêmes contraintes qu'un système de paiement ou qu'un moteur de recommandation.
| Besoin | Type | Exemples | Quand choisir |
|---|---|---|---|
| Transactions ACID, schéma structure | Relationnel | PostgreSQL, MySQL | Données critiques, relations complexes, reporting |
| Schéma flexible, hiérarchies | Document | MongoDB, CouchDB | Catalogue, profils utilisateurs, contenu |
| Cache, sessions, configuration | Key-value | Redis, etcd | Accès sub-milliseconde, données éphémères |
| Time-series, analytics, gros volume | Colonnes | Cassandra, ClickHouse | Métriques, logs, IoT, requêtes analytiques |
| Relations complexes, graphes | Graphe | Neo4j | Recommandations, fraude, réseaux sociaux |
| Recherche full-text | Recherche | Elasticsearch, Meilisearch | Autocompletion, facettes, similarite |
Relationnel (PostgreSQL, MySQL)¶
ACID complet, jointures, contraintes referentielles. La base par défaut quand les besoins ne sont pas encore clairs. PostgreSQL supporte aussi du JSON natif, des tableaux, et des extensions pour time-series (TimescaleDB) et recherche (pg_trgm).
Document (MongoDB, CouchDB)¶
Schémas flexibles sans migration de colonnes. Bon pour les entités avec des attributs variables ou imbriques. Attention aux jointures : elles n'existent pas nativement — il faut denormaliser ou accepter plusieurs requêtes.
Key-value (Redis, etcd)¶
Lectures et écritures en O(1). Redis sert de cache, de store de sessions, de queue légère, de pub/sub. etcd est specialise dans la configuration distribuée et la coordination de cluster.
Colonnes (Cassandra, ClickHouse)¶
Optimises pour les écritures massives et les lectures analytiques sur des colonnes spécifiques. Cassandra excelle sur les time-series et les données réparties geographiquement. ClickHouse sur les requêtes analytiques OLAP.
Graphe (Neo4j)¶
Les relations sont des citoyens de première classe. Quand votre domaine est intrinsequement un graphe (réseau social, arbre de dépendances, détection de fraude), un graphe bat un schéma relationnel sur les requêtes traversant plusieurs niveaux de relations.
Gouvernance du polyglot¶
Chaque base ajoutee a un coût opérationnel : backups, monitoring, migrations, mises à jour de sécurité, expertise interne. Questions a se poser :
- PostgreSQL avec les bonnes extensions ne suffit-il pas ?
- Le gain de performance est-il mesure où suppose ?
- L'équipe a-t-elle l'expertise pour opérer cette base en production ?
- Le gain justifie-t-il la complexité supplémentaire ?
Commencer simple
PostgreSQL couvre 80% des cas d'usage. Ne diversifiez que quand un besoin spécifique l'exige — chaque base supplémentaire est un système a opérer, a monitorer, a sauvegarder et a maintenir.
CAP et PACELC¶
Les théorèmes qui gouvernent les trade-offs dans les systèmes distribués.
Théorème CAP¶
En présence d'une partition réseau, un système distribué ne peut garantir que deux des trois propriétés :
- Consistency (C) : chaque lecture reçoit la donnée la plus récente ou une erreur
- Availability (A) : chaque requête reçoit une réponse (pas necessairement la plus récente)
- Partition tolérance (P) : le système continue malgre des pertes de messages réseau
La partition réseau est incontournable en distribué — le choix réel est entre CP et AP.
CP (Consistency + Partition tolérance) : le système refuse de répondre plutôt que de retourner une donnée incorrecte. PostgreSQL en mode synchrone, etcd, ZooKeeper. Pour les données financieres et les verrous distribués.
AP (Availability + Partition tolérance) : le système répond toujours, même avec une donnée potentiellement stale. Cassandra, DynamoDB. Pour les feeds, compteurs, préférences utilisateurs.
Extension PACELC¶
CAP ne parle que du comportement en cas de partition. PACELC couvre aussi le comportement normal :
- En cas de Partition (P) : trade-off entre Availability (A) et Consistency (C)
- Else, en opération normale (E) : trade-off entre Latency (L) et Consistency (C)
| Base | CAP | Opération normale | Comportement en partition |
|---|---|---|---|
| PostgreSQL | CP | Privilegier C | Refuse si replica pas synchronisé |
| Cassandra | AP | Privilegier L | Répond avec potentielle stale data |
| DynamoDB | AP | Configurable | Eventual consistency par défaut |
| etcd | CP | Privilegier C | Leader election, pas de split-brain |
| CouchDB | AP | Privilegier L | Merge des conflits à la reconnexion |
| MongoDB | CP | Privilegier C (configurable) | Bascule sur replica si primaire down |
Implications pratiques¶
Ne pas sur-optimiser pour le théorème : CAP décrit le comportement en cas de partition, événement rare dans un datacenter bien configuré. La majorité du temps, PACELC apporte plus de valeur.
Choisir en fonction du domaine : si votre domaine requiert ACID, re-implémenter ces garanties dans l'application est plus complexe et moins fiable qu'une base CP.
Configurer par opération : DynamoDB (strongly consistent reads) et Cassandra (CONSISTENCY_LEVEL par requête) permettent de choisir par opération. Mutations critiques en CP, lectures de cache en AP.
Partitioning¶
Distribuer les données sur plusieurs nœuds quand une seule machine ne suffit plus.
Pourquoi partitionner¶
Une seule machine atteint ses limites en volume de stockage, débit d'écriture, ou latence géographique. Le sharding distribué les données horizontalement, chaque nœud responsable d'un sous-ensemble.
Le sharding est différent de la réplication : la réplication duplique les mêmes données (haute disponibilité), le sharding divise en sous-ensembles distincts (scalabilité). Les deux sont utilisés ensemble : N shards, chacun répliqué M fois.
Stratégies de partitionnement¶
Hash-based : la partition key est hashee, le résultat déterminé le nœud. Distribution uniforme mais pas de range queries.
Range-based : répartition par plages de valeurs. Permet les range queries mais expose aux hot spots si les données récentes sont concentrees.
Geographic : répartition par region (EU, US, APAC). Réduit la latence locale et répond aux contraintes de souveraineté (RGPD).
Critères de choix de la partition key¶
Une bonne partition key a :
- Cardinalite élevée : suffisamment de valeurs distinctes pour distribuer sur N shards
- Distribution uniforme : éviter qu'un sous-ensemble concentre la majorité du trafic
- Alignement avec les accès patterns : les requêtes fréquentes ciblent un seul shard
| Partition key | Stratégie | Avantage | Risque |
|---|---|---|---|
customer_id | Hash | Distribution uniforme, isolation client | Cross-customer reports = scatter-gather |
created_at | Range | Range queries, archivage facile | Hot spot sur le shard "aujourd'hui" |
region | Geographic | Data locality, RGPD | Déséquilibre si une region domine |
order_id | Hash | Distribution maximale | Lister commandes d'un client = scatter |
Hot spots
Un mauvais choix de partition key crée des hot spots. Un status avec 3 valeurs possibles est une très mauvaise partition key — 90% des écritures iront sur pending.
Consistent hashing¶
Le hashing simple (hash(key) % N) pose un problème : quand on ajoute ou retire un nœud, presque toutes les clés changent de nœud. Le consistent hashing résout ca.
Principe de l'anneau¶
Les nœuds et les clés sont positionnes sur un anneau virtuel (espace de hash circulaire). Chaque clé est assignee au premier nœud rencontre dans le sens horaire.
graph TD
subgraph "Anneau de hash"
N1["Noeud A<br/>position 0"]
N2["Noeud B<br/>position 90"]
N3["Noeud C<br/>position 180"]
N4["Noeud D<br/>position 270"]
end
K1["Cle X<br/>hash=45"] --"→ Noeud B"--> N2
K2["Cle Y<br/>hash=200"] --"→ Noeud D"--> N4
K3["Cle Z<br/>hash=350"] --"→ Noeud A"--> N1 Quand un nœud est ajoute, seules les clés entre le nouveau nœud et son predecesseur migrent. Quand un nœud est retire, ses clés vont au nœud suivant. Le reste du système n'est pas impacte.
Virtual nodes (vnodes)¶
Un seul nœud physique est représenté par plusieurs points (vnodes) sur l'anneau. Ca amélioré l'équilibrage : un nœud avec plus de capacité reçoit plus de vnodes. Cassandra utilisé par défaut 256 vnodes par nœud.
Impact sur le resharding¶
Sans consistent hashing, ajouter un nœud nécessité de migrer une fraction importante des données. Avec consistent hashing, seule 1/N-ieme des données migré (N = nombre de nœuds). Avec des vnodes, la migration est encore plus fine et distribuée.
Cross-shard queries¶
Une requête qui touche plusieurs shards (scatter-gather) est plus coûteuse qu'une requête sur un seul shard — elle est envoyee en parallèle a tous les shards, les résultats sont fusionnes.
Stratégies pour réduire le scatter-gather¶
Denormalisation : dupliquer les données sur plusieurs shards pour éviter les jointures inter-shards. Le coût est la cohérence : les copies doivent être mises à jour de manière asynchrone.
Fan-out à l'écriture : écrire sur plusieurs shards lors de la mutation plutôt que de scatter à la lecture. Twitter utilise ce pattern pour les timelines : quand un utilisateur publie un tweet, il est écrit dans la timeline de chaque follower (fan-out on write). La lecture est O(1) par utilisateur.
Index global : maintenir un index secondaire qui mappe les attributs non-partition-key aux shards contenant les données. L'index est lui-même distribué et éventuellement consistant.
Pipeline analytique dédié : les requêtes aggregees (top-N, sommes globales) sont intrinsequement du scatter-gather. Les isoler dans un datawarehouse (BigQuery, Snowflake, ClickHouse) alimentee par CDC, plutôt que de les exécuter sur la base transactionnelle.
graph LR
subgraph "Transactionnel"
S1[(Shard 1)]
S2[(Shard 2)]
S3[(Shard 3)]
end
CDC[CDC / ETL] --"replique"--> DW[(Data Warehouse<br/>ClickHouse / BigQuery)]
S1 --> CDC
S2 --> CDC
S3 --> CDC
API[API Service] --"requete par shard key"--> S1
API --"requete par shard key"--> S2
Analytics[Dashboard] --"requete analytique"--> DW Données à grande échelle¶
Au-delà du partitionnement, gérer les données à grande échelle implique des stratégies supplémentaires.
Tiering de stockage¶
Toutes les données n'ont pas la même fréquence d'accès. Un tiering automatique déplacé les données vers le stockage le moins coûteux en fonction de leur âge :
| Tier | Stockage | Latence | Coût | Données |
|---|---|---|---|---|
| Hot | SSD, mémoire | ms | Élevé | Derniers jours/semaines |
| Warm | HDD, S3 Standard | Secondes | Moyen | Derniers mois |
| Cold | S3 Glacier, archivage | Minutes-h | Faible | Historique, conformité |
Compaction et TTL¶
Les bases distribuées accumulent des versions et des tombstones. La compaction fusionne les anciennes versions et purge les données expirees. Cassandra et Kafka utilisent la compaction de manière intensive.
Définir un TTL (Time To Live) sur les données éphémères : sessions (heures), métriques détaillées (jours), logs (semaines). Sans TTL, le volume croit indéfiniment et le coût de stockage explose.
Multi-region¶
Déployer les données sur plusieurs regions pour la latence et la résilience :
- Active-passive : une region principale, une region de secours. Basculement en cas de panne. Simple mais latence inter-region pour les écritures.
- Active-active : les deux regions acceptent les écritures. Complexe : conflits d'écriture, résolution (LWW, CRDT). CockroachDB et Spanner gèrent ca nativement.
Coût du multi-region
Le multi-region ajoute de la complexité, du coût réseau inter-region, et des problèmes de consistency supplémentaires. Ne le déployer que pour des besoins réels de latence géographique ou de conformité reglementaire. Pour la plupart des applications, un seul datacenter avec réplication locale suffit.
Patterns de consistency¶
Les modèles disponibles et leurs implications.
| Modèle | Garantie | Latence | Cas d'usage |
|---|---|---|---|
| Strong consistency | Toujours la donnée la plus récente | Élevée | Transactions bancaires, stocks |
| Eventual consistency | Les replicas convergent dans le temps | Faible | Feeds sociaux, compteurs, préférences |
| Causal consistency | Les opérations causalement liees sont ordonnées | Moyenne | Messaging, collaboration temps réel |
| Read-your-writes | Un utilisateur voit ses propres écritures | Variable | Sessions, profils |
| Monotonic reads | Pas de retour en arriere | Variable | Pagination, listings |
Tunable consistency¶
La plupart des systèmes modernes offrent de la consistency configurable par requête. Avec Cassandra :
ONE: un seul replica répond. Latence minimale, risque de stale.QUORUM: majorité des replicas. Bon équilibre latence/fraicheur.ALL: tous les replicas. Consistency maximale, disponibilité dégradée.
La règle : QUORUM en écriture + QUORUM en lecture garantit la strong consistency (les quorums se chevauchent). ONE en lecture + QUORUM en écriture donne de l'eventual consistency avec écriture fiable.
Conflits et résolution¶
Quand deux nœuds acceptent des écritures concurrentes sur la même clé :
- Last-write-wins (LWW) : la mutation la plus récente gagne. Simple mais les horloges distribuées ne sont jamais parfaitement synchronisées
- CRDT : structures de données conçues pour fusionner sans conflit (compteurs, sets, listes)
- Application-level merge : le code applicatif reçoit les deux versions et décidé. Le plus flexible mais le plus complexe
Eventual consistency est un choix délibéré
Eventual consistency n'est pas un défaut — c'est un choix délibéré pour gagner en latence et disponibilité. Le problème survient quand c'est un choix accidentel. Documenter le modèle de consistency de chaque opération dans votre API.
Chapitre suivant : CQRS et event sourcing — séparation lecture/écriture, projections et snapshots.