MongoDB et schémas de données flexibles
(Section : Technologie et infrastructure)
Résumé succinct
MongoDB est un stockage orienté documents avec des schémas flexibles (BSON), des inserts rapides, une mise à l'échelle horizontale et un puissant pipeline d'aggregation. Dans iGaming, il est idéal pour les profils de joueurs, les cartes CRM flexibles, les logs d'événements, la télémétrie, les projections matérialisées à partir de strim, les catalogues de jeux et les représentations cachées pour les fronts. Pour les invariants monétaires (portefeuille/ledger), il reste plus souvent un contour SQL/CP ; MongoDB convient en tant que modèle de lecture et de stockage documentaire haute performance.
Où MongoDB donne le maximum dans iGaming
Profils et paramètres des joueurs : variables de structure (paramètres locaux, préférences, métadonnées KYC).
Catalogues de contenu/jeux/fournisseurs : lecture rapide des cartes, filtres, balisage, texte intégral.
Événements/télémétrie/journaux : TPS élevés, fenêtres temporelles, stockage TTL.
Représentations matérialisées (CQRS) : écrans rapides (leaders, actions récentes, agrégats).
Personnalisation/fiches en ligne ML : modèles KV dans les collections, court TTL.
Principes du régime flexible : la discipline au lieu du chaos
MongoDB n'est pas « sans schéma » - le schéma vit dans le code et la validation.
Recommandé :1. Schéma comme contrat : JSON Schema Validation dans les collections.
2. Versioner les documents avec le champ 'schemaVersion'.
3. Champs obligatoires stricts (id, clés de recherche), « queue » des attributs rares - facultatif.
4. Limite la dimension et l'imbrication des tableaux (pour les index et RAM).
5. Migrations en arrière-plan : Apdates par « schemaVersion », Shedulers, back-fill.
Exemple : JSON Schema Validation
js db. createCollection("player_profiles", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["playerId", "createdAt", "schemaVersion"],
properties: {
playerId: { bsonType: "string" },
createdAt: { bsonType: "date" },
schemaVersion: { bsonType: "int", minimum: 1 },
locale: { bsonType: "string" },
kyc: {
bsonType: "object",
properties: {
status: { enum: ["pending", "verified", "rejected"] },
doc: { bsonType: "object" }
}
}
}
}
}
});
Modèle de données et conception de documents
Concevoir « à la demande » : 1 écran/endpoint = 1 document ou un petit ensemble de documents.
Dénormalisation : incluez les petits sous-papiers imbriqués (par exemple, les mini-cartes des fournisseurs de jeux).
- Intégration - pour les tranches étroitement liées et rarement mises à jour.
- Les références ('ref') sont à grande taille/fréquentes updates/réutilisation.
- Limite de taille : Document ≤ 16 Mo ; les grands binaires sont GridFS/objets de stockage.
- Audit/métadonnées : 'createdAt', 'updatedAt', 'traceId', 'tenantId', 'idempotencyKey'.
Indices : qualité de lecture et stabilité de latitude
Types d'indices et de pratiques :- B-Tree (principal)
Composer : l'ordre des champs correspond à des prédicats et des triages fréquents.
Règle prefix : Les options préfixées fonctionnent pour '(tenantId, playerId, createdAt)'.
Trier : tenez compte de 'sort' à la fin de l'index (par exemple, 'createdAt : -1').
js db. bets. createIndex(
{ tenantId: 1, playerId: 1, createdAt: -1 },
{ name: "idx_bets_tenant_player_created_desc" }
);
Partial / Sparse
Accélérer les sous-ensembles fréquents ('status : « pending »), réduire la taille.
js db. withdrawals. createIndex(
{ playerId: 1, createdAt: -1 },
{ partialFilterExpression: { status: "pending" } }
);
TTL
Pour la télémétrie/logs/fiches temporelles, l'expiration automatique.
js db. events. createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 });
Texte/autocoplete
'text' pour le texte intégral (restrictions linguistiques) ; pour le remplissage automatique - 'n-gram '/trigram à travers les champs et les approches regex ou Atlas Search.
Antécédents d'index
L'indice « pour tout » → la baisse de la vitesse d'enregistrement.
Faible cardinalité sans partialité → faible sélectivité.
Composites en double.
Indexer les champs à l'intérieur des masses géantes sans limites.
Aggregation Pipeline : écrans rapides et rapports
Utilisez '$ match' → '$ sort' → '$ limit' comme étapes initiales ; concevoir des index sous '$ match/$ sort'.
'$ lookup 'pour les joyaux contrôlés (doux, en volumes raisonnables).
'$ facet' pour les métriques multiples ; '$ unionWith' est une fusion de collections.
'$ merge '/' $ out' est la matérialisation des résultats dans la collection (read-models).
js db. bets. aggregate([
{ $match: { tenantId: "eu-1", playerId: "p123" } },
{ $sort: { createdAt: -1 } },
{ $limit: 100 },
{ $group: {
_id: "$playerId",
lastBets: { $push: { amount: "$amount", ts: "$createdAt", game: "$gameId" } },
totalAmount: { $sum: "$amount" }
} }
]);
Transactions, cohérence et idempotence
Single-document atomic - atomicité libre ; invariants complexes - pensez à diviser par documents.
Transactions multi-documents (ACID) - il y a des réseaux de répliques, mais plus chers en latitude ; appliquer par points.
- « w : » majority « » pour les enregistrements critiques (coût de la latitude) ;
- « readConcert : » majority « » pour une lecture cohérente.
- Idempotence : clés uniques sur 'idempotencyKey '/' pspTx', opération UPSERT ('$ setOnInsert', '$ inc').
js db. wallet. updateOne(
{ playerId: "p123" },
{ $inc: { balanceCents: -5000 }, $set: { updatedAt: new Date() } },
{ upsert: true, writeConcern: { w: "majority" } }
);
Chardonnage et sélection de clés
MongoDB chardonne sur la clé shard. Le choix est critique :- Répartition de la charge : clé d'une cardinalité élevée et d'une distribution uniforme (par exemple, '(tenantId, playerId)').
- Évitez la monotonie : 'CreatedAt'comme seule clé → boule « chaude ».
- Hashed - distribue les enregistrements de manière plus uniforme.
- Ranged - mieux pour les demandes de gamme, mais regardez les queues chaudes.
- Zone de partage (tag ranges) pour la réglementation/localisation (EU/LatAm/TR).
js sh. enableSharding("igaming");
db. bets. createIndex({ tenantId: 1, playerId: 1, _id: "hashed" });
sh. shardCollection("igaming. bets", { tenantId: 1, playerId: 1, _id: "hashed" });
Antichambre :
- La clé shard par faible cardinalité (« status ») est une distorsion des chards.
- Fréquents '$ lookup' entre les collections chardonnées sans co-chardage sur une seule clé.
- Clé shard modifiable (difficile et coûteuse à changer).
Replica-sets, lectures et politiques read-after-write
Réplique-set = HA et base de transaction.
Read Preference:- « primary » pour les lectures critiques ;
- "primaryPreferred'/" secondaire" - pour les analystes/non critiques.
- Lisez/Écrivez dernier accord avec SLO et le budget de latitude.
Modification des flux, CDC et intégration
Change Streams : Abonnement insertion/update/suppression - pratique pour :- Synchronisation des couches cache (Redis),
- déclencheurs CRM/notifications,
- téléchargements en OLAP (ClickHouse/Pinot),
- écrans à réaction.
- Modèle Outbox : pour les domaines critiques, publiez les événements dans une collection distincte, qui est ensuite lue par le connecteur et diffusée sur le bus (Kafka). Cela améliore la prévisibilité des intégrations.
Observabilité et SLO
SLO : p99 cartes de lecture ≤ 10-20 ms ; insertion d'événements ≤ 20-40 ms ; La différence de leitensi entre les chardes dans X %; disponibilité ≥ 99. 9%.
Métriques : op-latence, queue depth, % de yumps par statistiques secondaires, cache/WT, page fautes, lock-waits, kol dans les curseurs/connexions ouverts.
Profilage : 'system. profile ',' explain (« executionStats ») ', verrouillage des collections/index.
Alert : croissance de WT cache pressure, opérations lentes, croissance des requêtes qui ne sont pas entrées dans l'indice, retard des migrations secondaires, chunk/balancier.
Performances et tuning
WiredTiger Cache : par défaut, ~ 50 % RAM - validez sous le profil.
Compression : snappy/zstd pour les collections, zstd pour les magazines - équilibre CPU/IO.
Inserts Batch et bulkWrite pour la télémétrie.
Projection ('{field : 1}') pour ne pas traîner de documents « épais ».
Limit/Skip : évitez les grands 'skip' → utilisez la pagination par curseur/par marqueur ('createdAt/_ id').
Collections cappées pour les loges « anneaux ».
Sécurité et conformité
Auth/RBAC : rôles sur les collections/OBD, privilèges minimes nécessaires.
TLS en transit, cryptage sur disque (FLE/at-rest).
Politiques PII : masquage/pseudonymisation, collections séparées pour les champs sensibles.
Multi-tenance : préfixes/bases de données/collections individuelles, filtres par 'tenantId', vous pouvez des couches RLS similaires dans l'application.
Vérification : inclure une vérification des opérations sur les collections critiques.
Backups, PITR et DR
Snapshots (snapshots) volumes + oplog-backaps pour Point-in-Time Recovery.
Replica set dans une autre région pour DR ; exercices réguliers de reconstruction.
Contrôle de la croissance oplog sous les pics d'insertion (PSP webhooks/tournois).
Les clusters shard sont des backups cohérents avec le serveur bou.
Intégration avec le reste de l'architecture
CQRS : les commandes frappent SQL (argent), les événements → Materialized Views dans MongoDB.
Event-Streaming : Kafka/Pulsar comme pneu, Mongo - sink/source via connecteurs et Change Streams.
Redis : à côté d'une couche de latence ultra-basse (caches/compteurs).
OLAP : déchargement à ClickHouse/Pinot pour les scans longs et BI.
Chèque d'implémentation
1. Fixez les domaines : ce qui se passe dans Mongo (flexible/haute TPS/projection), ce qui reste dans SQL.
2. Définissez schema contracts : JSON Schema Validation, 'schemaVersion'.
3. Concevoir des index pour les demandes réelles ; ajoutez un TTL pour les données « bruyantes ».
4. Choisissez la clé de shard (cardinalité élevée, uniformité) ; si nécessaire, zone-charding.
5. Personnalisez le jeu de répliques, Read/Write Concert sous SLO ; la politique de read-after-write.
6. Inclure l'observation et le profilage, les alertes par index/WT cache/oplog.
7. Organisez des backups + PITR, un cluster DR et des exercices réguliers.
8. Connectez Change Streams/Outbox pour synchroniser les caches et les bus.
9. Limitez la taille et l'imbrication des documents ; introduire la pagination sur le curseur.
10. Politiques distinctes pour les IPI/tenants, cryptage, audit.
Anti-modèles
« Pas de schéma » dans la vente : l'absence de validation et de versions → chaos.
La clé shard dans le temps/monotone est une boule chaude et une p99 instable.
Joynes '$ lookup' sur d'énormes ensembles sans index/pagination.
Utiliser les transactions partout - perte de productivité.
L'absence de TTL/rétroaction pour les loges → une augmentation du volume et du coût.
Stocker des invariants monétaires critiques uniquement dans Mongo sans impotence stricte.
Résultats
MongoDB est un outil puissant pour les domaines flexibles iGaming : profils, catalogues, télémétrie, projections et personnalisation. La clé du succès est le schéma-contrat et la validation, l'indexation réfléchie, la clé bien choisie, Read/Write Concert, Change Streams pour les intégrations et la discipline opérationnelle rigoureuse (observabilité, backups, DR). Combiné avec le noyau SQL et le bus de streaming, cela donne à la plate-forme des interfaces rapides et résistantes aux pics de tournoi.