GH GambleHub

MongoDB і гнучкі схеми даних

(Розділ: Технології та Інфраструктура)

Коротке резюме

MongoDB - документно-орієнтоване сховище з гнучкими схемами (BSON), швидкими вставками, горизонтальним масштабуванням і потужним Aggregation Pipeline. У iGaming воно відмінно підходить для профілів гравців, гнучких CRM-карток, логів подій, телеметрії, матеріалізованих проекцій зі стріму, каталогів ігор і кешованих уявлень для фронтів. Для грошових інваріантів (гаманці/ledger) частіше залишається SQL/CP-контур; MongoDB доречна як read-model і високопродуктивне документне сховище.


Де MongoDB дає максимум в iGaming

Профілі та налаштування гравців: змінні структури (налаштування локалі, уподобання, KYC-метадані).
Каталоги контенту/ігор/провайдерів: швидке читання карток, фільтри, тегування, повнотекст.
Події/телеметрія/журнали: високі TPS, тимчасові вікна, TTL-зберігання.
Матеріалізовані представлення (CQRS): швидкі екрани (лідерборди, останні дії, агрегати).
Персоналізація/фічі онлайн-ML: KV-патерни в колекціях, короткий TTL.


Принципи гнучкої схеми: дисципліна замість хаосу

MongoDB не «без схеми» - схема живе в коді і валідації.

Рекомендується:

1. Схема як контракт: JSON Schema Validation в колекціях.

2. Версіонування документів полем'schemaVersion'.

3. Строгі обов'язкові поля (id, ключі пошуку), «хвіст» рідкісних атрибутів - опціонально.

4. Обмеження розмірності масивів і вкладеності (для індексів і RAM).

5. Міграції в тлі: апдейти по'schemaVersion', шедулери, бек-філли.

Приклад: 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" }
}
}
}
}
}
});

Модель даних і проектування документів

Проектуйте «під запит»: 1 екран/ендпойнт = 1 документ або невеликий набір документів.
Денормалізація: включайте невеликі вкладені піддокументи (наприклад, міні-картки провайдерів ігор).

Посилання vs вбудовування:
  • Вбудовування - для тісно пов'язаних і рідко оновлюваних фрагментів.
  • Посилання ('ref') - при великому розмірі/частих апдейтах/повторному використанні.
  • Обмеження розміру: документ ≤ 16 МБ; великі бінарники - GridFS/об'єктні сховища.
  • Аудит/метадані: `createdAt`, `updatedAt`, `traceId`, `tenantId`, `idempotencyKey`.

Індекси: якість читання і стабільність latency

Типи індексів і практики:
  • B-Tree (основний)

Compound: порядок полів відповідає частим предикатам і сортуванням.
Prefix-правило: для'( tenantId, playerId, createdAt)'працюють префіксні варіанти.
Сортування: враховуйте'sort'в кінці індексу (наприклад,'createdAt: -1`).

js db.bets.createIndex(
{ tenantId: 1, playerId: 1, createdAt: -1 },
{ name: "idx_bets_tenant_player_created_desc" }
);

Partial / Sparse

Прискорюють часті підмножини ('status: «pending»'), зменшують розмір.

js db.withdrawals.createIndex(
{ playerId: 1, createdAt: -1 },
{ partialFilterExpression: { status: "pending" } }
);

TTL

Для телеметрії/логів/часових фічів - автоматичне закінчення.

js db.events.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 });

Текстовий/autocomplete

'text'для повнотексту (обмеження щодо мов); для автодоповнення -'n-gram '/trigram через поля і regex-підходи або Atlas Search.

Антипатерни індексів

Індекс «на все» → падіння швидкості запису.
Низька кардинальність без partial → низька селективність.
Дублюючі компаунди.
Індексувати поля всередині гігантських масивів без лімітів.


Aggregation Pipeline: швидкі екрани та звіти

Використовуйте " " " " "як ранні стадії; проектуйте індекси під'$match/ $sort'.
«$lookup» для контрольованих джойнів (м'які, в розумних обсягах).
«$facet» для множинних метрик; «$unionWith» - об'єднання колекцій.
«$merge »/« $out» - матеріалізація результатів у колекції (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" }
} }
]);

Транзакції, узгодженість та ідемпотентність

Single-document atomic - безкоштовна атомарність; складні інваріанти - думайте про розбиття по документах.
Multi-document transactions (ACID) - є з репліка-сетами, але дорожче по latency; застосовувати точково.

Write Concern / Read Concern:
  • `w: «majority»'для критичних записів (вартість latency);
  • `readConcern: «majority»'для узгодженого читання.
  • Ідемпотентність: унікальні ключі на'idempotencyKey '/' pspTx', UPSERT-операції ('$setOnInsert','$inc').
Приклад UPSERT:
js db.wallet.updateOne(
{ playerId: "p123" },
{ $inc: { balanceCents: -5000 }, $set: { updatedAt: new Date() } },
{ upsert: true, writeConcern: { w: "majority" } }
);
💡 Для грошових інваріантів зазвичай віддають перевагу SQL. У Mongo - тільки при суворій дисципліні ключів/ідемпотентності та обмежених транзакціях.

Шардування і вибір ключів

MongoDB шардує по shard key. Вибір критичний:
  • Розподіл навантаження: ключ високої кардинальності і рівномірним розподілом (наприклад,'( tenantId, playerId)').
  • Уникайте монотонності: 'createdAt'як єдиний ключ → «гарячий» шард.
Діапазони vs хеш:
  • Hashed - рівніше розподіляє записи.
  • Ranged - краще для діапазонних запитів, але стежте за гарячими хвостами.
  • Зона-шардинг (tag ranges) для регуляторики/локалізації (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" });
Антипатерни:
  • Шард-ключ за низькою кардинальністю («status») - перекіс шардів.
  • Часті «$lookup» між шардованими колекціями без співшардування по одному ключу.
  • Змінюваний shard key (міняти складно і дорого).

Репліка-сети, читання і політика read-after-write

Репліка-сет = HA і основа транзакцій.

Read Preference:
  • 'primary'для критичних read-after-write;
  • 'primaryPreferred '/' secondary'- для аналітики/не критичних.
  • Read/Write concern узгоджуйте з SLO і бюджетом latency.

Change Streams, CDC та інтеграції

Change Streams: підписка на вставки/апдейти/видалення - зручно для:
  • синхронізації кеш-шарів (Redis),
  • тригерів CRM/повідомлень,
  • завантаження в OLAP (ClickHouse/Pinot),
  • Реактивні екрани.
  • Outbox-патерн: для критичних доменів публікуйте події в окрему колекцію, яку потім читає конектор і транслює в шину (Kafka). Це підвищує передбачуваність інтеграцій.

Спостережуваність і SLO

SLO: p99 читання карток ≤ 10-20 мс; вставки подій ≤ 20-40 мс; різниця лейтенсі між шардами в межах X%; доступність ≥ 99. 9%.
Метрики: оп-латентність, queue depth,% юмпсів на вторинні, cache/WT статистики, page faults, lock-waits, кількість відкритих курсорів/з'єднань.
Профілювання: `system. profile','explain («executionStats»)', блокування колекцій/індексів.
Алерти: зростання WT cache pressure, повільні операції, зростання не потрапили в індекс запитів, відставання вторинних, chunk migrations/балансер.


Продуктивність і тюнінг

WiredTiger Cache: за замовчуванням ~ 50% RAM - валідуйте під профіль.
Compression: snappy/zstd для колекцій, zstd для журналів - баланс CPU/IO.
Batch-вставки і bulkWrite для телеметрії.
Projection (`{field:1}') щоб не тягти «товсті» документи.
Limit/Skip: уникайте великих'skip'→ використовуйте пагінацію по курсору/по маркеру ('createdAt/_ id').
Capped колекції для «кільцевих» логів.


Безпека та комплаєнс

Auth/RBAC: ролі на колекції/БД, мінімально необхідні привілеї.
TLS в транзиті, шифрування на диску (FLE/at-rest).
Політики PII: маскування/псевдонімізація, окремі колекції для чутливих полів.
Мульти-тенантність: префікси/окремі БД/колекції, фільтри по'tenantId', можна RLS-подібні шари в додатку.
Аудит: включайте аудит операцій на критичних колекціях.


Бекапи, PITR і DR

Знімки (snapshots) томів + oplog-бекапи для Point-in-Time Recovery.
Репліка-сет в іншому регіоні для DR; регулярні навчання відновлення.
Контроль зростання oplog під піки вставок (PSP вебхуки/турніри).
У шард-кластерах - узгоджені бекапи з config-сервером.


Інтеграція з іншою архітектурою

CQRS: команди б'ють по SQL (гроші), події → Materialized Views в MongoDB.
Event-Streaming: Kafka/Pulsar як шина, Mongo - sink/source через конектори і Change Streams.
Redis: поруч як шар ультра-низької латентності (кеші/лічильники).
OLAP: вивантаження в ClickHouse/Pinot для довгих сканів і BI.


Чек-лист впровадження

1. Зафіксуйте домени: що йде в Mongo (гнучко/високий TPS/проекції), що залишається в SQL.
2. Визначте schema contracts: JSON Schema Validation, `schemaVersion`.
3. Спроектуйте індекси під реальні запити; додайте TTL для «галасливих» даних.
4. Виберіть shard key (висока кардинальність, рівномірність); при необхідності - zone-шардинг.
5. Налаштуйте репліка-сет, Read/Write Concern під SLO; політика read-after-write.
6. Увімкніть спостережуваність і профілювання, алерти на індекси/WT cache/oplog.
7. Організуйте бекапи + PITR, DR-кластер і регулярні навчання.
8. Підключіть Change Streams/Outbox для синхронізації кешів і шин.
9. Обмежте розмір документів і вкладеність; впроваджуйте пагінацію по курсору.
10. Окремі політики для PII/тенантів, шифрування, аудит.


Антипатерни

«Без схеми» в проді: відсутність валідації і версій → хаос.
Шард-ключ за часом/монотонний - гарячий шард і нестабільна p99.
Джойни «$lookup» на величезних наборах без індексів/пагінації.
Використовувати транзакції повсюдно - втрати продуктивності.
Відсутність TTL/ретенції для логів → зростання обсягу і вартості.
Зберігати критично важливі грошові інваріанти тільки в Mongo без суворої ідемпотентності.


Підсумки

MongoDB - потужний інструмент для гнучких доменів iGaming: профілі, каталоги, телеметрія, проекції та персоналізація. Ключ до успіху - схема-контракти і валідація, продумана індексація, грамотно обраний shard key, усвідомлені Read/Write Concern, Change Streams для інтеграцій і жорстка експлуатаційна дисципліна (спостережуваність, бекапи, DR). У поєднанні з SQL-ядром і стрімінговою шиною це дає платформі швидкі інтерфейси і стійкість під турнірні піки.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.