Этот вопрос бьет точно в цель. Архитектура баз данных для аналитики (OLAP/Data Warehouse) и архитектура для высоконагруженных игровых серверов (OLTP/Real-time) — это две разные вещи.
Если коротко: Эта структура идеальна как “Архив” (Historical Database) для игрового сервера, но она не должна использоваться как “Оперативная память” (Live State) во время самой игры.
Давай разберем почему, и как это устроено на серверах масштаба Lichess или Chess.com.
Почему эта схема не идеальна для записи ходов в реальном времени (каждую секунду):
Представь, что у нас идет 1000 партий одновременно. Каждую секунду игроки делают ходы. Чтобы записать ход в нашу базу “на лету”, серверу нужно:
- Вычислить FEN-строку.
- Сделать запрос в таблицу
positions: “А есть ли уже такой FEN в базе?” - Если нет — сделать
INSERTи получить новыйid. - Сделать
INSERTв таблицуturnsс этимid.
Делать такие проверки (особенно шаг 2 и 3 с уникальными индексами) тысячи раз в секунду — это тяжело для любой базы данных (возникают блокировки). Дедупликация позиций в реальном времени “тормозит” быстрый поток игры.
Как это решается на практике (Теория архитектуры игровых серверов)
Настоящие игровые сервера разделяют хранилища:
-
Оперативное хранилище (Live Games) Пока партия идет, база данных (PostgreSQL) вообще не дергается для каждого хода! Вся партия живет в оперативной памяти (в твоем Scala/Ox движке) или в очень быстром хранилище типа Redis. Ходы просто записываются в массив строк (
b1c3,e2e4) в памяти. Если сервер упадет, Redis мгновенно восстановит игру. Это позволяет держать десятки тысяч одновременных партий вообще без задержек диска. -
Аналитическое хранилище (Game Archive — наша текущая БД) Как только кто-то ставит мат или сдается, партия официально завершается. В этот момент игровой сервер берет всю сыгранную партию целиком, упаковывает ее в условный JSON и отправляет “на задний фон” (в очередь задач, например RabbitMQ/Kafka).
И вот там фоновый воркер спокойно, не мешая играющим людям:
- Распаковывает партию.
- Проходит по всем ходам.
- Аккуратно закидывает новые позиции в таблицу
positions(дедуплицируя их). - Записывает все данные в таблицу
gamesиturns.
Итог для твоего гипотетического сервера:
Наша текущая структура великолепно подойдет для игрового сервера, но играть роль она будет именно хранилища сыгранных партий.
Игровой движок будет вести партию в памяти/Redis, а после завершения — сохранять её в нашу спроектированную PostgreSQL базу для того, чтобы игроки могли посмотреть историю, а боты — собрать статистику и аналитику.
Так что мы на правильном пути. Эта структура закладывает мощный аналитический фундамент.
Есть ли еще теоретические сомнения, или можем взглянуть на таблицу games?