Синхронність проти Асинхронності: Як бази даних визначають стабільність ігрового TPS
Коли ігровий сервер починає страждати від просадок TPS (Ticks Per Second) або короткочасних фризів, адміністратори насамперед дивляться на графіки завантаження процесора (CPU) та оперативної пам'яті (RAM). Однак часто статистика хостингу показує ідеальну картину: процесор завантажений лише на 30%, вільної пам'яті з надлишком, але гравці продовжують скаржитися на затримки при відкритті інвентарів, «застигання» світу та лаги реєстрації дій.
У такій ситуації справжній винуватець ховається на стику ігрового рушія та системи керування базами даних (СУБД). Неоптимальна архітектура запитів до MySQL, PostgreSQL, MongoDB або SQLite здатна повністю паралізувати найпотужніше багатоядерне обладнання. У цій статті ми проведемо детальний архітектурний розбір взаємодії ігрового потоку з базами даних і зрозуміємо, як швидкість дисків хостингу визначає плавність гри.
1. Ігровий потік і пастка синхронності (Блокування I/O)
Як ми вже знаємо, ядро більшості ігрових серверів працює в один основний потік (Main Thread). Цей потік циклічно виконує ігровий код і має жорсткий часовий ліміт на один кадр — наприклад, рівно 20 мілісекунд для досягнення стабільного TPS у Minecraft або 33.3 мілісекунди для Rust.
Коли програміст використовує синхронний (блокуючий) запит до бази даних, ігровий потік буквально завмирає. Розглянемо, що відбувається під капотом сервера, коли гравець заходить на сервер і скрипт намагається завантажити його профіль:
- Ігровий потік доходить до рядка коду:
LoadPlayerData(playerID);. - Сервер призупиняє симуляцію ігрового світу. Час кадру продовжує тикати.
- Движок відправляє запит до СУБД (наприклад, MySQL) і переходить у режим очікування.
- СУБД починає шукати потрібний запис на диску хостингу, зчитує її та відправляє відповідь назад ігровому рушію.
- Тільки після отримання відповіді ігровий потік «прокидається» і продовжує симулювати світ.
Якщо цей процес зайняв 50 мілісекунд (через відсутність індексів у таблиці або повільний диск), поточний серверний кадр триватиме не положені 20 мс, а всі 70 мс. Сервер пропустить кілька тіків, а гравці в цей момент відчують різкий фриз (Stutter).
2. Що таке I/O Bottleneck і чому процесор відпочиває, поки сервер лагає?
Термін I/O Bottleneck (вузьке горлишко введення-виведення) описує ситуацію, за якої швидкість роботи всієї системи обмежується повільною швидкістю читання/запису накопичувача (SSD/HDD).
Коли сервер виконує синхронні запити, процесор хостингу фактично не здійснює жодної складної математичної роботи — він просто перебуває в стані очікування (iowait). Саме тому на графіках панелі керування ви бачите низьке завантаження CPU. Процесор вільний, але він зв'язаний по руках і ногах повільним диском.
Якщо на сервері одночасно грають 100 осіб, і мод виконує дрібні синхронні запити UPDATE при кожному отриманні предмета або вбивстві боса, диск починає захлинатися від кількості операцій введення-виведення в секунду (IOPS). Запити вишикуються в величезну чергу, час очікування кожного кадру зростає експоненціально, і сервер уходить у глибокий мережевий анабіоз.
3. Асинхронність — паралельні світи обчислень
Щоб врятувати ігровий потік від дискового рабства, сучасні архітектури використовують асинхронні (неблокуючі) запити. При такому підході плагін або рушій гри взагалі не чекає відповіді від бази даних у головному потоці.
Алгоритм асинхронної взаємодії кардинально відрізняється:
- Ігровий потік викликає асинхронну команду (наприклад,
mysql_tqueryв SA:MP або фонові таски в C#/Java). - Головний потік моментально забуває про цей запит і відразу ж продовжує обробляти гру (фізика вважається, гравці бігають, TPS стабільний).
- Сам запит передається у **Фоновий потік (Worker Thread)**, який працює на іншому, вільному ядрі процесора. Цей потік бере на себе всю рутину спілкування з СУБД та диском.
- Коли диск хостингу нарешті віддав дані, фоновий потік посилає сигнал головному потоку і повертає результат через так званий Колбек (Callback) — функцію зворотного виклику, яка безпечно видає гравцеві його речі.
4. Як різні СУБД впливають на логіку сервера?
Вибір архітектури бази даних на хостингу безпосередньо визначає, з якими типами навантажень зіткнеться ваш проєкт.
MySQL і PostgreSQL (Реляційні СУБД)
Ідеально підходять для зберігання жорстко структурованих даних (акаунти, логи, системи донату). Вони вимагають обов'язкової індексації полів пошуку (наприклад, за нікнеймом або ID). Без індексів реляційна СУБД при кожному запиті скануватиме всю таблицю з диска зверху вниз, викликаючи миттєвий I/O Bottleneck.
MongoDB (Документоорієнтована NoSQL СУБД)
Зберігає дані у форматі JSON-подібних документів. Вона чудово підходить для складних систем, що динамічно змінюються (наприклад, комплексний інвентар гравця з безліччю кастомних характеристик предметів). MongoDB відмінно масштабується і часто працює швидше з оперативною пам'яттю, але вимагає грамотного налаштування кешування.
SQLite (Локальна файл-БД)
Уся база даних зберігається в одному єдиному файлі прямо в папці мода. SQLite — головне джерело лагів на серверах із високим онлайном. Архітектура SQLite влаштована так, що при записі даних одного гравця вона блокує весь файл бази даних цілком. Якщо два гравці спробують одночасно зберегти прогрес, другий чекатиме, поки перший допише файл на диск, викликаючи каскадну затримку ігрового кадру.
| Тип СУБД | Режим блокування файлів диска | Застосовність для High-Load проєктів |
|---|---|---|
| MySQL / PostgreSQL | Рядкове блокування (Блокується лише один акаунт, інші доступні). | Висока (При асинхронному коді та індексах) |
| MongoDB | Документне блокування (Найвища паралельність за рахунок роботи через RAM-кеш). | Висока (Ідеально для інвентарів та крафту) |
| SQLite | Повне блокування файлу (Один пише — всі інші чекають). | Низька (Тільки для тестів або серверів до 15–20 слотів) |
Підсумок
Стабільність ігрового TPS залежить не тільки від обчислювальної потужності процесора, а й від того, наскільки ефективно цей процесор вміє делегувати завдання. Проєктування взаємодії з базами даних має будуватися на повному виключенні синхронних запитів із головного ігрового циклу. Залиште ігровому потоку симуляцію геометрії та ПВП, а всю важку рутину читання та запису файлів довірте фоновим потокам та швидким NVMe-накопичувачам вашого ігрового хостингу.