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` → `$limit` как ранние стадии; проектируйте индексы под `$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).

Нажимая кнопку, вы соглашаетесь на обработку данных.