На одному VPS з 16 ГБ можна обслуговувати багато бізнесу. До того моменту, поки не стане неможливо. Незапланований відмову рідко буває «він одразу впав». Гірше: він кульгає, потім ваш додаток створює ще більше підключень, і база даних перетворюється на завантажений ресторан з одним кухарем і 200 людьми із картками.
Саме тут «MySQL проти MariaDB» перестає бути дебатом про дизайни і стає операційним рішенням: яка СУБД дає безпечніші дефолти, чіткішу спостережуваність і менше сюрпризів під час додавання реплікації та пулінгу — бо ви додасте їх, якщо дбаєте про доступність.
Реальність VPS з 16 ГБ: ви не маєте великого запасу потужності
VPS з 16 ГБ — це «середній седан» у хостингу баз даних. Достатньо, щоб відчувати впевненість. Одночасно це досить мало, щоб один неправильний тюнінг перетворився на інцидент із завантаженістю ЦП, голодуванням I/O, активним свопом та питанням «чому load average 40?».
Ось що насправді ви купуєте з 16 ГБ:
- Бюджет пам’яті: не 16 ГБ. Ядро, кеш файлової системи, ваш зоопарк агентів і сама база хочуть свою частку. Розраховуйте на 12–13 ГБ придатних для процесів MySQL/MariaDB, перш ніж ризикувати свопом і нестабільністю.
- Потолок I/O: VPS «NVMe» часто означає «спільний NVMe». Спайки латентності — не баг; це ваш сусід.
- Конкуренція за ЦП: навіть при виділених vCPU шумні сусіди проявляються як %steal, затримки планувальника і непередбачувана хвістова латентність.
- Радіус ураження одного хоста: kernel panic, корупція файлової системи, технічні роботи провайдера, випадкове rm — і ви офлайн.
На такому сервері реплікація і пулінг — це не «покращення продуктивності». Це різниця між поганим днем і кар’єрно обмежувальним днем.
Факти та історія, які й досі важливі в продакшені
Деякі контекстні пункти — тривія, поки вони не вдарять вас під час вікна оновлення.
- MariaDB відгалузилася від MySQL у 2009 році після поглинання Sun компанією Oracle. Цей форк був не лише про ідеологію; він визначив темп релізів і дефолти.
- MySQL 5.6/5.7 зробили InnoDB безперечним центром; ера MyISAM переважно в минулому, але багато застарілих схем досі носять відповідні припущення.
- MariaDB довше підтримувала інші інженси зберігання (і додала нові). Це сила й ризик: більше опцій — більше шансів обрати невірно.
- MySQL 8.0 повністю прибрав query cache (це була глобальна mutex-вечірка). MariaDB довше підтримувала його версію, але при високій конкуренції він рідко приносить користь.
- GTID реалізації відрізняються між MySQL та MariaDB. «GTID увімкнено» — не кінець розмови; це початок питань сумісності.
- За замовчуванням плагін автентифікації MySQL змінився (caching_sha2_password став більш поширеним). Старі клієнти та деякі проксі мали з цим проблеми.
- MariaDB і MySQL з часом розійшлися в поведінці оптимізатора. Один і той же SQL може мати різні плани. На VPS регрес плану дорожчий, бо місця для маневру немає.
- Реплікація еволюціонувала з «best effort» у операційну основу: безпечні метадані реплікації, багатопотокові аплаєри, опції semi-sync. Але вам усе одно потрібно тестувати відмовостійкість серйозно.
MySQL проти MariaDB: як обирати на VPS
Формулювання рішення: гарантії операційності, а не список фіч
На VPS з 16 ГБ вас обмежують дві речі: латентність і варіативність. Пікова пропускна здатність менш важлива за стабільний час відгуку під навантаженням і безпечне відновлення, коли щось іде не так.
Тому обирайте движок, виходячи з:
- Безпечності оновлень: наскільки передбачувані мажорні/мінорні оновлення і наскільки добре команда їх розуміє?
- Сумісності інструментів: чи поводяться ваш проксі, інструменти бекапу, ORM і моніторинг стабільно?
- Ергономіки реплікації: чи отримуєте ви чіткий статус реплікації, просте пересівання і менше граничних сюрпризів?
- Стабільності продуктивності: чи уникає він патологічних зависань (metadata locks, flushing stalls, thrash буферного пулу) при вашому навантаженні?
MySQL: «нудний дефолт», і він нудний не просто так
Якщо у вас типовий веб-навантаження (таблиці InnoDB, OLTP-запити, трохи звітності), MySQL 8.0 часто є безпечним вибором. Він широко протестований у екосистемі, з проксі, драйверами та сервісами. Операційно це важить більше, ніж блискуча фічі, яку ви ніколи не застосуєте.
Переваги MySQL на VPS:
- Передбачувана підтримка екосистеми: драйвери, конектори та хмарні інструменти частіше орієнтовані спочатку на MySQL.
- Зрілість Performance Schema: відмінно підходить для діагностики чекань, блокувань і «топових» SQL без здогадок.
- «Нормальність» реплікації: багато інструментів для фейловера та рукописних рукбуків написані з урахуванням семантики MySQL.
MariaDB: сильний варіант, але сприймайте сумісність як проєкт
MariaDB може бути чудовим вибором, коли потрібні її специфічні можливості або в організації вже є стандартизація на ній. Але не думайте, що це «MySQL з іншим логотипом». Розбіжності реальні, і на малому хості операційні витрати сюрпризів зростають.
Переваги MariaDB на VPS:
- Інші варіанти реплікації та кластеризації: MariaDB + Galera — поширений патерн (з застереженнями), і можливості реплікації MariaDB можуть виглядати привабливими залежно від версії.
- Рухливість спільноти та пакування: деякі дистрибутиви та вендори мають сильні дефолти та бекпорти для MariaDB.
Думка: Якщо ви починаєте з нуля і ваш стек типовий (веб-додаток + проксі + стандартні конектори), оберіть MySQL 8.0. Якщо ви вже стабільно працюєте на MariaDB — не мігруйте просто з нудьги; робіть це, коли маєте чітку проблему і протестований план.
Цитата про надійність, яку варто записати на стікері
Werner Vogels: «Все ламається постійно.» (парафразована ідея)
Коли пулінг підключень стає обов’язковим (а не «приємним»)
На VPS з 16 ГБ ваш сервер бази даних не лише виконує запити. Він ще й няньчиться з підключеннями. Потоки, пам’ять на підключення, контекстні переключення, рукопашні аутентифікації, TLS та внутрішня бухгалтерія. Ці накладні витрати невидимі аж до моменту, коли вони раптово проявляються.
Пулінг стає обов’язковим, коли ви бачите будь-який із цих патернів
- Шторм підключень: автоскейл інстансів додатку, cron-налагодження, деплоя чи ретраїв створюють сплески сотень/тисяч нових підключень за секунду.
- Багато коротких запитів: класичний веб-OLTP, де запити швидкі, але численні; накладні витрати на підключення стають вузьким місцем.
- Висока конкурентність на малому хості: навіть якщо кожен запит дешевий, БД витрачає час на планування, а не на роботу.
- Чутливість до хвостової латентності: вам важливі p95/p99, а не середнє. Налаштування підключення і планування потоків псуватимуть хвости.
Чесна правда: якщо ваш додаток відкриває і закриває підключення на запит, пулінг не опціональний. БД рано чи пізно почне тримати вас на уздрі CPU, mutex-контенцією або пам’ятним тиском. І зробить це в найнеприємніший момент.
Жарт #1: База без пулінгу — як нічний клуб з охоронцем, який перевіряє ваш документ щоразу, коли ви моргнули.
Що означає «пулінг» у продакшені
Є два рівні:
- Пулінг на боці додатку (базовий і бажаний): ваш додаток підтримує стабільний пул підключень на інстанс. Це добре, але масштабування лінійно з кількістю інстансів.
- Пулінг/проксі на мережевому рівні (обов’язковий в міру зростання): pooler/proxy, як-от ProxySQL, мультиплексує багато сесій клієнта на меншу кількість серверних підключень, застосовує правила маршрутизації і захищає БД від штормів.
Розмір пулу на VPS з 16 ГБ: перестаньте думати «max_connections = 2000»
Високе max_connections — не гордість. Часто це визнання того, що ви не зробили пулінг і компенсуєте відмовою.
На 16 ГБ зазвичай бажано:
- Розумна кількість серверних підключень: часто у сотнях або менше, залежно від навантаження.
- Строгі таймаути: вбивайте ідл-клієнтів раніше, ніж вони перетворяться на витік ресурсів.
- Зворотний тиск: пулери, які ставлять у чергу і скидатимуть навантаження замість того, щоб дозволити БД померти.
Коли реплікація стає обов’язковою (навіть якщо ви боїтеся складності)
Реплікація — це не лише «для масштабування читань». На одному VPS вона передусім про виживання: бекапи, час відновлення, техобслуговування і тестування. Перший раз, коли ваш єдиний хост відмовить і відновлення займе години, ви полюбите репліки.
Реплікація стає обов’язковою, коли вам потрібне будь-що з переліченого
- RPO < 1 година без ставок на бінлог-бекапи і ідеальні процедури відновлення.
- Вікна технічного обслуговування без простоїв: оновлення ОС, апгрейди БД, міграція сховища.
- Бекапи, що не давлять продакшен: робіть логічні бекапи або запускайте важкі читання проти репліки.
- Безпечні експерименти: тестуйте зміни в схемах, плани запитів, побудову індексів.
- Розумні дії при інциденті: потрібне чисте місце для судових запитів без боротьби з живим трафіком.
Репліка на іншому VPS: мінімально життєздатна архітектура
Для primary з 16 ГБ поширений «дорослий» крок такий:
- Primary (16GB): приймає записи
- Replica (8–16GB): обслуговує читання, виконує бекапи, може бути піднята в роль primary
- Pooler/proxy (малий VM або на хостах додатка): контролює конкурентність і маршрутизацію
Це не гламурно. Це надійно. Як вогнегасник: нудно, поки не стане абсолютно потрібним.
Асинхронна vs semi-sync реплікація на VPS
Асинхронна — дефолт: primary повертає успіх до того, як репліка підтвердить. Швидко, але можна втратити останні транзакції при падінні primary.
Semi-sync може зменшити цей ризик, чекаючи підтвердження хоча б однієї репліки перед комітом. На VPS з мережевою варіативністю semi-sync також може посилити хвостову латентність. Використовуйте його, коли ви можете терпіти цю латентність і маєте стабільне з’єднання з реплікою; інакше зосередьтеся на добрих бекапах і швидкому фейловері.
Жарт #2: Реплікація — як чищення зубів: всі обіцяють почати після першої надзвичайної ситуації.
Швидкий план діагностики: знайти вузьке місце швидко
Коли латентність стрибає або пропускна здатність падає, не починайте з редагування конфігу. Почніть з того, щоб з’ясувати, що робить сервер зараз. Це найкоротший шлях від паніки до правильного рішення.
Перше: це CPU, пам’ять чи I/O?
- Насичення CPU / черга виконання: якщо CPU завантажений і load average високий, шукайте дорогі запити, mutex-чекання або занадто багато потоків.
- Тиск пам’яті: якщо йде своп або reclaim, ви не просто тюните — ви тонеєте. Зменшіть використання пам’яті й кількість підключень, потім перегляньте розміри буферів InnoDB.
- Латентність I/O: якщо час очікування диска зростає, ви можете мати багато CPU і все одно бути «вниз». Поведінка InnoDB при flush, doublewrite, частота fsync та сусідські шумові операції мають значення.
Друге: занадто багато підключень чи занадто мало ресурсів на підключення?
- Перевірте кількість потоків, активні підключення, aborted connections.
- Перевірте, чи блокуються запити через блокування або очікують на I/O.
- Шукайте шторм підключень після деплоїв і подій автоскейлу.
Третє: регрес плану запиту чи борг у схемі/індексах?
- Визначте топ-запити за загальним часом і p95 латентністю.
- Підтвердіть використання індексів і оцінки рядків.
- Шукайте відсутні складені індекси й випадкові повні скани.
Четверте: стан реплікації (якщо вона є)
- Затримка репліки: кілька секунд відставання від джерела, ріст relay log.
- Аплаєрні потоки застрягли на транзакції, очікування блокувань.
- Диск на репліці повний через binlogs/relay logs.
Практичні завдання: команди, приклади виводів та рішення
Ось реальні перевірки, які я виконую на VPS з 16 ГБ, коли щось відчувається неправильно. Кожна містить команду, типовий вивід, що це означає і яке рішення я приймаю.
Завдання 1: Підтвердити тиск пам’яті та активність свопу
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 15Gi 12Gi 520Mi 310Mi 2.6Gi 1.8Gi
Swap: 2.0Gi 1.2Gi 820Mi
Що це означає: Своп використовується активно. «Available» маленький. Очікуйте затримок, особливо під навантаженням записами.
Рішення: Негайно зменшити кількість підключень і пам’ять на підключення. Якщо ви використовуєте великі sort/join буфери, зупиніть це. Потім правильно визначте розмір InnoDB buffer pool і впровадьте пулінг.
Завдання 2: Перевірити насичення CPU і %steal
cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.1.0 (server) 12/31/2025 _x86_64_ (4 CPU)
12:00:01 AM CPU %usr %sys %iowait %steal %idle
12:00:02 AM all 72.4 10.8 1.2 8.6 7.0
12:00:03 AM all 70.1 11.2 0.9 9.3 8.5
Що це означає: CPU сильно завантажений і %steal високий. Ви конкуруєте за CPU хоста.
Рішення: Зменшіть конкурентність (пулінг), знизьте max_connections і пріоритезуйте ефективність запитів. Розгляньте перенесення БД на тариф з меншим контеншеном або з виділеними CPU.
Завдання 3: Швидка перевірка латентності диска
cr0x@server:~$ iostat -x 1 3
Device r/s w/s rkB/s wkB/s await svctm %util
nvme0n1 35.2 210.7 2200.1 9800.4 18.40 0.52 96.8
Що це означає: Диск майже насичений і середній await високий. InnoDB буде зупинятися на fsync і flush операціях.
Рішення: Дослідіть write amplification (binlogs, doublewrite, redo), перевірте flush-поведінку «dirty pages» і зменшіть сплески (пакетування, розмір транзакцій). Якщо це шум від спільного сховища, реплікація не вирішить проблему; потрібне краще сховище.
Завдання 4: Підтвердити версію MySQL/MariaDB і движок
cr0x@server:~$ mysql -e "SELECT VERSION() AS version, @@version_comment AS comment\G"
*************************** 1. row ***************************
version: 8.0.36
comment: MySQL Community Server - GPL
Що це означає: Точна «марка» і версія визначають, які лічильники продуктивності, поля статусу реплікації і дефолти застосовуються.
Рішення: Узгодьте кроки усунення неполадок з версією. Не застосовуйте поради для MariaDB до MySQL 8.0 без перевірки (і навпаки).
Завдання 5: Перевірити тиск підключень і використання потоків
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_%'; SHOW VARIABLES LIKE 'max_connections';"
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 12 |
| Threads_connected | 385 |
| Threads_created | 98123 |
| Threads_running | 42 |
+-------------------+-------+
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 800 |
+-----------------+-------+
Що це означає: Багато створених потоків свідчить про churn. Велика кількість підключених потоків на малому сервері часто — накладні витрати, а не корисна робота.
Рішення: Запровадьте пулінг. Зменшіть max_connections. Встановіть адекватні таймаути. Якщо churn потоків високий, перевірте thread_cache_size і повторне використання підключень.
Завдання 6: Виявити топ чекань (Performance Schema)
cr0x@server:~$ mysql -e "SELECT event_name, COUNT_STAR, SUM_TIMER_WAIT/1000000000000 AS total_s
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE event_name LIKE 'wait/io/file/innodb/%'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 5;"
+----------------------------------------+------------+---------+
| event_name | COUNT_STAR | total_s |
+----------------------------------------+------------+---------+
| wait/io/file/innodb/innodb_log_file | 8421132 | 912.21 |
| wait/io/file/innodb/innodb_data_file | 20122341 | 311.45 |
| wait/io/file/innodb/innodb_temp_file | 1023311 | 88.17 |
+----------------------------------------+------------+---------+
Що це означає: Сильні чекання на log file вказують на fsync/логовий тиск. Чекання на data file — на I/O-залежність читань/записів.
Рішення: Якщо домінують чекання на лог, перегляньте розмір транзакцій, налаштування durability і розмір redo log. Якщо домінують чекання на дані, гляньте на hit rate buffer pool і плани запитів.
Завдання 7: Перевірити ефективність InnoDB buffer pool
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name | Value |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests | 987654321 |
| Innodb_buffer_pool_reads | 12345678 |
+---------------------------------------+-----------+
Що це означає: Співвідношення read_requests до reads дає приблизний коефіцієнт промахів кеша. На VPS промахи коштують дорого через сховище.
Рішення: Якщо промахів багато і пам’ять доступна — збільште buffer pool. Якщо пам’яті немає — виправляйте запити/індекси і зменшуйте робочий набір.
Завдання 8: Знайти найгірші запити за загальним часом
cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_s, ROUND(AVG_TIMER_WAIT/1e9,2) AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 3\G"
*************************** 1. row ***************************
DIGEST_TEXT: SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT ?
COUNT_STAR: 1243321
total_s: 812.44
avg_ms: 0.65
Що це означає: Запит дешевий у середньому за виклик, але домінує за часом через обсяг. Це історія про пулінг і індексацію, а не про «більший сервер».
Рішення: Переконайтеся в наявності коректного складеного індексу (user_id, created_at). Розгляньте кешування на рівні додатку. Додайте пулінг, щоб уникнути накладних витрат підключень при високому QPS.
Завдання 9: Швидко знайти блокування
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G"
TRANSACTIONS
------------
Trx id counter 123456789
Purge done for trx's n:o < 123456700 undo n:o < 0 state: running
History list length 9821
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
... WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 456 n bits 72 index PRIMARY of table `app`.`accounts` trx id 123456780 lock_mode X locks rec but not gap waiting
Що це означає: Deadlock-и і чекання свідчать про конфліктні записи або довгі транзакції, що утримують блокування.
Рішення: Скоротіть транзакції, додайте правильні індекси і зробіть оновлення детермінованими. Якщо ORM виконує «select then update» патерни, виправте це.
Завдання 10: Перевірити статус реплікації (MySQL)
cr0x@replica:~$ mysql -e "SHOW REPLICA STATUS\G"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 4
Last_SQL_Error:
Що це означає: Репліка здорова з невеликим лагом.
Рішення: Ви можете безпечно знімати читання/бекапи з репліки. Якщо лаг зростає — припиніть навантажувати її важкими читаннями і перевірте аплаєр/IO вузькі місця.
Завдання 11: Підтвердити зберігання binlog та тиск на диск
cr0x@server:~$ mysql -e "SHOW BINARY LOGS;"
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| binlog.000231 | 1073741824|
| binlog.000232 | 1073741824|
| binlog.000233 | 1073741824|
+------------------+-----------+
Що це означає: Binlog-и великі і численні. На диску VPS binlog-и тихо можуть з’їсти кореневий файловий простір і перетворити гарний тиждень на цикл перезавантажень.
Рішення: Встановіть явну політику зберігання (expire settings), моніторте використання файлової системи і переконайтеся, що репліки не блокують видалення через офлайн-стан.
Завдання 12: Перевірити ємність файлової системи та виснаження inode
cr0x@server:~$ df -h /var/lib/mysql
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 200G 186G 14G 94% /
Що це означає: Ви близькі до повного диска. MySQL почне поводитися неправильно задовго до 100%, бо йому потрібне місце для тимчасових таблиць, логів і відновлення після аварій.
Рішення: Звільніть місце зараз. Видаліть старі логи, перемістіть бекапи поза хост, збільшіть диск або зменшіть час зберігання. Якщо у бізнес-години ви вже на 94% — ви запізнилися.
Завдання 13: Перевірити тиск тимчасових таблиць (пам’ять vs диск)
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 842113 |
| Created_tmp_tables | 902331 |
+-------------------------+----------+
Що це означає: Високе відношення tmp disk tables означає, що запити паркуються на диск. На VPS це податок на латентність.
Рішення: Додайте індекси, зменшіть розмір результатів, виправте GROUP BY/ORDER BY патерни і уникайте великих сортувань. Не «вирішуйте» це сліпим збільшенням tmp_table_size, поки не впевнені, що не досягнете OOM.
Завдання 14: Перевірити розміри таблиць/індексів, щоб зрозуміти робочий набір
cr0x@server:~$ mysql -e "SELECT table_schema, table_name,
ROUND((data_length+index_length)/1024/1024,1) AS mb
FROM information_schema.tables
WHERE table_schema='app'
ORDER BY (data_length+index_length) DESC
LIMIT 5;"
+-------------+----------------+------+
| table_schema| table_name | mb |
+-------------+----------------+------+
| app | events | 8420 |
| app | orders | 3120 |
| app | users | 980 |
+-------------+----------------+------+
Що це означає: Ваші найбільші таблиці визначають потреби buffer pool. Якщо «гарячий» набір не поміщається — ви завжди платитимете за I/O.
Рішення: Розділіть/архівуйте холоді дані, додайте зведені таблиці або винесіть аналітику окремо. Масштабування вгору не вирішить вічно зростаючий робочий набір на одному VPS.
Три корпоративні міні-історії з реального життя
Міні-історія 1: Інцидент через неправильне припущення
Вони запускали SaaS-додаток на одному 16 ГБ VM. Все працювало місяцями. З’явилася нова інтеграція, і команда припустила: «база може обробляти більше підключень; це ж просто параметр». Вони підняли max_connections і продовжили роботу.
Наступного понеділка трафік підскочив. Додаток відкривав нові підключення для кожного запиту, бо пулінг був неправильно налаштований і фактично вимкнений. База приймала підключення, бо й звісно вона це робила. Вона також створювала потоки, виділяла буфери на підключення і почала контекстні переключення як схвильований гудзончик.
Латентність зросла. Додаток робив ретраї. Це створило ще більше підключень. ОС почала свопити. Тепер кожен запит мав «особливу приправу» — page faults пам’яті. Інженери дивилися на графіки CPU і не розуміли, чому «додавання підключень» не додало пропускної здатності.
Виправлення не було героїчним тюнінгом. Вони ввели пулінг, зменшили max_connections до значення, яке машина могла реально витримати, і додали невеликий проксі-слой для поглинання сплесків. Далі з’явилася реплікація — не для читань, а щоб робити бекапи без того, щоб тягнути продакшен в кисіль.
Міні-історія 2: Оптимізація, що відкотилася
Інша компанія мала повільні звітні запити. Хтось вирішив, що найкраще — збільшити тимчасові таблиці і більше пам’яті для сортувань. Підняли session і global буфери агресивно: sort buffers, join buffers, temp table sizes. Звіти стали швидшими на стейджингу. Усі аплодували і відправили на продакшен.
Продакшен не впав миттєво. Він «впав чемно». Під конкурентністю кожне підключення могло виділяти великі шматки пам’яті. Сотні підключень помножені на «розумні» пер-канекшн буфери стали «сюрприз, у вас закінчилась пам’ять». Сервер почав свопити. Потім база вдарилася у стіну зависань: I/O злетів, fsync-и в черзі, а час відгуку прагнув до нескінченності.
Команда ганялася за привидами: мережа? провайдер? ядро? Насправді вони перетворили контрольований бюджет пам’яті на рулетку пер-канекшн алокацій.
Откат був принижувальним: зменшили буфери на підключення, використали правильні індекси, переписали звіти і винесли важку звітність на репліку. Вони також усвідомили просту істину: на малих хостах передбачувані обмеження ресурсів краще за «швидко коли сам» кожного разу.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Ще одна команда тримала MySQL з реплікою в іншому регіоні. Нічого особливого: асинхронна реплікація, щоденні логічні бекапи і щотижневий тест відновлення. Ніхто не хвалився цим на зустрічах, бо це було занадто нудно, щоб вражати.
Потім у них стався інцидент зі сховищем на primary VPS. Файлова система стала лише для читання посеред робочого дня. База зробила те, що роблять бази в такому стані: поскаржилася, перестала просувати транзакції і залишила всіх вибирати між простоєм і поганими рішеннями.
Вони не панікували і не почали тюнити. Вони підняли репліку згідно з відпрацьованим рукописом, перелаштували додаток на новий primary через пулер і вивели старий primary з ротації. Сервіс дещо деградував; він не впав. Пізніше вони перебудували старий ноду з нуля і чисто пересіяли дані.
Ключова деталь: вони репетирували. Нудний тест відновлення означав, що репліка була придатна, облікові дані працювали, і додаток міг вказати в інше місце без трьохгодинної археології конфігів.
Поширені помилки: симптом → причина → виправлення
Саме тут більшість розгортань баз на VPS з 16 ГБ гине: не від однієї великої помилки, а від кількох операційних непорозумінь.
1) Симптом: раптове зростання латентності після деплою
Причина: шторм підключень (нові поди додатку стартують, кожен відкриває пул; або пулінг вимкнений і підключення створюються на запит).
Виправлення: застосуйте пулінг, обмежте конкуренцію на проксі, налаштуйте wait_timeout/interactive_timeout належно і тримайте max_connections реалістичним. Якщо використовуєте ProxySQL, налаштуйте мультиплексування уважно і протестуйте з вашими транзакційними патернами.
2) Симптом: CPU високий, QPS на місці, «Threads_running» високий
Причина: занадто багато конкурентних запитів, блокування або регрес плану запиту, що викликає дорогі скани.
Виправлення: визначте топ-digest-и, додайте індекси, зменшіть конкуренцію через пулінг і виправте довгі транзакції. Не «вирішуйте» підняттям CPU, поки не з’ясуєте, чому потоки виконуються.
3) Симптом: load average величезний, але CPU не повністю зайнятий
Причина: I/O wait або блоковані потоки (fsync-перекоси, jitter диска, metadata locks).
Виправлення: використайте iostat -x, перевірте чекання Performance Schema і шукайте довгі DDL або беккап-операції на primary. Виносьте бекапи і важкі читання на репліку.
4) Симптом: затримка репліки зростає під час піку
Причина: I/O вузьке місце репліки, одно-поточний аплаєр, або важкі читання, що відбирають ресурси.
Виправлення: збільшіть ресурси репліки або зменшіть її читачеве навантаження; налаштуйте паралельну реплікацію там, де можливо; переконайтеся, що relay logs і диск не насичені; уникайте запуску масивних звітів на тій же репліці, що використовується для фейловера.
5) Симптом: помилки «Too many connections» хоча сервер здається простим
Причина: витоки підключень або довго живучі транзакції, що фіксують підключення; виснаження пулу; max_connections занадто малий для реального патерну конкуренції.
Виправлення: знайдіть клієнта з витоком, застосуйте таймаути і поставте проксі попереду, щоб ставити в чергу. Підвищуйте max_connections лише після того, як обмежили пам’ять на підключення і стабілізували пулінг.
6) Симптом: періодичні зупинки кожні кілька хвилин
Причина: контрольні точки або flush-стали, переливи в тимчасові таблиці, або фонові завдання, що роблять сплески.
Виправлення: згладьте записову роботу, перевірте поведінку dirty pages, уникайте великих транзакцій і розкладіть батч-джоби. На VPS сховище карає великі сплески латентністю.
Чеклісти / покроковий план
Крок за кроком: зробіть пулінг реальністю на VPS з 16 ГБ
- Інвентаризація клієнтів: перелічіть всі сервіси/джоби, що підключаються до БД (додаток, воркери, cron, BI-інструменти).
- Спочатку виправте пулінг у додатку: встановіть явний розмір пулу, max lifetime і idle timeout. Уникайте «необмежених пулів».
- Поставте проксі/пулер, коли у вас є кілька нод додатку або непередбачувані сплески.
- Встановіть server max_connections на число, яке хост може дозволити. Це запобіжник, а не ручка продуктивності.
- Налаштуйте таймаути, щоб вбивати ідл-підключення і завислі сесії.
- Моніторте Threads_created, Threads_connected, aborted connects. Бажані — стабільні, нудні графіки.
Крок за кроком: додайте реплікацію, не перетворіть це на хобі
- Виберіть топологію: один primary + одна репліка — базова конфігурація. Тримайте просто.
- Розмістіть репліку в окремій домені відмови: інший VPS хост, бажано інша зона провайдера.
- Увімкніть binary logging і встановіть зберігання відповідно до потреб відновлення та розміру диска.
- Засійте репліку правильно: використайте консистентний знімок (фізичний бекап-інструмент або логічний дамп з правильними lock/GTID-параметрами).
- Переконайтеся в здоров’ї реплікації і налаштуйте алерти на lag та помилки.
- Напишіть і відрепетируйте кроки промоції. Повторіть тест при оновленнях версій.
- Перенесіть бекапи і важкі читання на репліку. Тримайте фейловер-репліку чистою і не перевантаженою.
Крок за кроком: налаштуйте InnoDB для VPS з 16 ГБ без самонанесення шкоди
- Правильно розмірюйте buffer pool виходячи з пам’яті вільної і робочого набору. Не позбавляйте повністю OS cache.
- Тримайте пер-канекшн буфери помірними. Глобальні буфери безпечніші за сюрпризи per-connection.
- Слідкуйте за переливом тимчасових таблиць і виправляйте запити перед тим, як роздувати ліміти пам’яті.
- Підтвердіть налаштування durability відповідно до бізнес-вимог. Не копіюйте небезпечні налаштування тільки, щоб виграти бенчмарки.
- Міряйте, міняйте одне і знову міряйте. «Тюнінг», що змінює 30 параметрів одразу, — шлях до створення таємниць.
FAQ
1) Що обрати: MySQL чи MariaDB для одного VPS з 16 ГБ?
Якщо ви починаєте новий проєкт і хочете найбільш плавну підтримку екосистеми — обирайте MySQL 8.0. Якщо ви вже стабільно працюєте на MariaDB — залишайтеся, поки немає чіткої сумісності або операційної причини для міграції.
2) Коли пулінг підключень справді обов’язковий?
Коли у вас є сплески, багато інстансів додатку або короткі запити на високому QPS. Якщо ви бачите churn потоків (Threads_created швидко зростає) або шторм підключень під час деплоїв — це вже обов’язково.
3) Чи можна просто підняти max_connections замість пулінгу?
Можна, так само як можна прибрати пожежний датчик, бо він занадто голосний. Вищий max_connections часто збільшує використання пам’яті і контекстні переключення, погіршуючи інциденти.
4) Реплікація лише для масштабування читань?
Ні. На VPS реплікація перш за все для відновлення, бекапів і обслуговування. Масштабування читань — приємний побічний ефект, а не основна мотивація.
5) Асинхронна реплікація ризикована. Чи варто використовувати semi-sync?
Можливо. Semi-sync може зменшити втрату даних при падінні primary, але на VPS це може додати хвостової латентності. Вибирайте за RPO і стабільністю мережі, а потім тестуйте під навантаженням.
6) Чому моя репліка відстає під час звітів?
Тому що читання конкурують з аплаєром реплікації за CPU і I/O. Якщо репліка планується як target для фейловера, тримайте важку звітність подалі від неї або забезпечте окрему аналітичну репліку.
7) Який перший метрик дивитися на малому DB-хості?
Латентність диска (iostat await / utilization) і тиск пам’яті (активність свопу). Графіки CPU можуть вводити в оману, коли реальний вузький момент — I/O wait або свопінг.
8) Galera (MariaDB) — хороша ідея на VPS?
Може працювати, але це не безкоштовний обід. Синхронно-подібна координація записів може бити по латентності, а ризики split-brain зростають, якщо кворум спроектований неправильно. Якщо ви не готові оперувати кластером — спочатку робіть primary+replica.
9) Чи потрібен ProxySQL, якщо додаток вже має пул?
Не завжди, але часто. Пулінг в додатку все ще масштабує серверні підключення з кількістю інстансів додатку. Проксі додає зворотний тиск, маршрутизацію, мультиплексування і захист від штормів.
10) Яке найшвидше безпечне покращення, яке можна зробити цього місяця?
Додайте репліку для бекапів і відпрацьованого фейловера, і впровадьте пулінг підключень з суворими лімітами. Ці два кроки запобігають найбільш поширеним інцидентам «один VPS розплавився».
Висновок: що зробити наступного тижня
Якщо ви на VPS з 16 ГБ, у вас немає розкоші неакуратної конкуренції. Також у вас немає розкоші думати «ми додамо реплікацію пізніше». «Пізніше» — це коли ви відновлюєтеся з бекапу, а ваш CEO дізнається нові слова.
Практичні кроки:
- Впровадьте пулінг (на рівні додатку негайно; додайте проксі, якщо у вас є кілька нод додатку або сплески трафіку).
- Встановіть реалістичні ліміти підключень і таймаути; ставте
max_connectionsяк запобіжник. - Додайте репліку на окремому VPS і перенесіть туди бекапи/звітність.
- Напишіть runbook для фейловера і відрепетируйте його у робочий час з захисними механізмами.
- Проведіть швидку діагностику під час наступного сплеску латентності і документуйте висновки.
MySQL проти MariaDB самі по собі не врятують вас. Реплікація і пулінг — врятують. Оберіть ту СУБД, яку ваша команда вміє спокійно експлуатувати о 2:00 ночі, а потім інженеруйте систему так, щоб 2:00 ночі залишались нудними.