MariaDB vs PostgreSQL на 8GB VPS: як безпечно масштабувати клієнтів

Було корисно?

Ви не «закінчуєтеся база даних». Ви закінчуєтеся в чомусь нудному: оперативна пам’ять, IOPS, дескриптори файлів, процесорний час або терпіння.
На 8GB VPS межа між «працює» і «чому оформлення замовлення таймаутиться» тонша, ніж сарказм в каналі інцидентів.

Це польовий посібник з безпечного масштабування клієнтів для MariaDB і PostgreSQL, коли неможливо просто кинути залізо на проблему.
Ми поговоримо про обмеження з’єднань, арифметику пам’яті, реальність I/O, підводні камені, що виявляються під навантаженням, і практики, які тримають вас подалі від проблем.

Окреслити проблему: що насправді ламає «масштабування клієнтів»

«Масштабувати клієнтів» зазвичай означає «збільшити кількість одночасних з’єднань або запитів». Звучить як мережева проблема.
Це не так. Це проблема обліку ресурсів.

Типова схема відмови на 8GB VPS виглядає так:

  • Трафік зростає, сервери додатків відкривають більше з’єднань до БД «на всяк випадок».
  • БД їх приймає (бо значення за замовчуванням щедрі або ви поспішно підняли max_connections).
  • Пам’ять зростає на кожне з’єднання/сесію. Збільшується перемикання контексту. Кеші стискаються.
  • I/O піки, бо робочий набір більше не вміщується в RAM.
  • Зростає затримка; таймаути в додатку викликають повторні спроби; повтори створюють більше з’єднань; ви самі влаштовуєте собі denial-of-service.

На VPS є ще один нюанс: steal CPU, галасливі сусіди, кредитні бусти та сховище, яке «виглядає як SSD», але поводиться як сумна флешка у найгірший момент.

Цитата, яку варто приклеїти біля дашборда: Надія — не стратегія. — генерал Гордон Р. Салліван.
Це не цитата про бази даних, але найкраща інженерна цитата для масштабування БД.

Головне питання не «яка з них швидша — MariaDB чи PostgreSQL?». Питання:
яка система дозволить вам встановлювати розумні обмеження і отримувати передбачувану поведінку при зростанні клієнтів на обмеженій пам’яті та реальних дисках.

Факти та історія, які мають значення в продакшн

Це не питання для вікторини. Вони пояснюють, чому значення за замовчуванням, поведінка та інструменти операцій виглядають саме так.

  1. Історично модель PostgreSQL — «один бекенд на з’єднання».
    Такий дизайн робить ізоляцію та відлагодження простими, але змушує вас використовувати пулювання при зростанні конкуренції.
  2. Підхід MySQL з потоками на з’єднання сформував історію конкурентності MariaDB.
    Він може обробляти багато з’єднань, але пам’ять на сесію все ще може розчавити 8GB коробку, якщо дозволити це.
  3. MariaDB з’явився через придбання MySQL Oracle (епоха 2009–2010).
    Цей форк створив два екосистеми й два набори операційних припущень, які досі відрізняються.
  4. InnoDB став дефолтним движком MySQL значно пізніше, ніж домінував MyISAM.
    Багато постів про «тонке налаштування MySQL» — артефакти ери MyISAM і шкідливі для сучасних InnoDB-навантажень.
  5. MVCC PostgreSQL має операційну реальність — «bloat».
    Vacuuming не опціональний при інтенсивних update/delete; це збір сміття для таблиць.
  6. InnoDB у MariaDB також має MVCC і purge, але операційні важелі інші.
    Історія важлива: ручки не тотожні, і симптоми вводять в оману при міграції між ними.
  7. WAL PostgreSQL і redo/binlogs MariaDB перетворюють випадкові записи на послідовні-ish I/O.
    Але поведінка fsync, чекпоінтингу та «flush storm» все ще відрізняється по движках і конфігураціях.
  8. PostgreSQL у останні роки значно покращив паралельні запити.
    Це допомагає аналітиці, але може здивувати на малих машинах, якщо паралельні воркери їдять CPU і пам’ять під час піку OLTP.
  9. Пул потоків MariaDB існує саме тому, що модель потоків на з’єднання досягає стелі масштабування.
    Це один з небагатьох «увімкни — потім перевіряй» перемикачів, який реально змінює поведінку конкурентності.

Жарт №1: База даних завжди прийме вашу зміну max_connections=2000. Це як дитина, що погоджується на третю еспресо.

Як MariaDB і PostgreSQL витрачають ваші 8GB

Бюджет 8GB VPS: що ви насправді маєте

«8GB RAM» — це маркетинг. У продакшн ви резервуєте місце для ядра, файлового кешу, фонового сервісу і враховуєте, що використання пам’яті під час піку зростає.
Якщо БД працює самостійно, ви можете безпечно виділити близько ~5–6GB для налаштувань пам’яті бази даних.
Якщо ви запускаєте додаток + БД на одному VPS (не робіть цього, якщо можна уникнути), скоротіть цю цифру значно.

Ви також купуєте I/O, а не лише RAM. Більшість багів з затримкою, що звинувачують «продуктивність бази даних», насправді пов’язані з продуктивністю сховища та чергуванням.

PostgreSQL: передбачувано, але «важке» в контексті з’єднань

Пам’ять PostgreSQL загалом ділиться на:

  • Спільна пам’ять: головним чином shared_buffers.
  • Пам’ять на сесію / операцію: work_mem, maintenance_work_mem, сорти, хеші, тимчасові буфери.
  • Кеш сторінок ОС: PostgreSQL сильно покладається на нього для реальної продуктивності.

Головна проблема на малих машинах — не shared_buffers, а множення накладних витрат на сесію разом із виділенням пам’яті на запити.
Якщо дозволити сотням прямих підключень з додатків, ви ставитеся до аптайму як до ставки «вони не всі одночасно виконуватимуть дорогі запити».
Вони будуть. У понеділок. Під час розрахунку зарплат. Або коли хтось викатає новий звіт.

MariaDB (InnoDB): буферний пул і несподіванки на потоки

Для MariaDB (InnoDB-навантажень) пам’ять зазвичай домінує:

  • InnoDB buffer pool: ваш основний кеш (innodb_buffer_pool_size).
  • Буфери на з’єднання: sort buffer, join buffer, tmp tables, net buffers.
  • Кеші потоків і внутрішні кеші: особливо помітні при високій зміні з’єднань.

MariaDB може виглядати «добре», доки якийсь запит не змусить великі sort/join буфери в багатьох потоках і ви раптово не опинитесь у свопі.
Swap на VPS-backed диску — генератор повільних простоїв.

Безпечне масштабування клієнтів — це в основному контроль конкурентності

Обидві бази можуть витримувати серйозний трафік на 8GB, якщо ви зробите одну річ: обмежте конкуренцію і згладьте навантаження.
Пулювання з’єднань, розумні ліміти і дисципліна у запитах перемагають героїчне налаштування щоразу.

Швидкий план діагностики (перший/другий/третій)

Коли зростає затримка, потрібен швидкий цикл триажу, що показує, де ліміт: CPU, пам’ять, блокування чи I/O.
Не «налаштовуйте». Діагностуйте.

Перший: хоста хворий?

  • Середнє навантаження vs кількість CPU: якщо load високий, а CPU простає — ви чекаєте I/O.
  • Активність свопу: будь-який стійкий swap-in/out під час піку означає програш.
  • Затримка диска: якщо await висока, БД невинна; місце зберігання — місце злочину.

Другий: БД стоїть у черзі чи блокується?

  • PostgreSQL: перевірте pg_stat_activity на події очікування і заблоковані запити.
  • MariaDB: перевірте processlist і InnoDB status на очікування блокувань та дедлоки.
  • Шторм з’єднань: таймаути + повтори створюють самонаведене навантаження. Шукайте раптові стрибки кількості з’єднань.

Третій: це один поганий запит чи системне налаштування?

  • Логи повільних запитів (обох) покажуть, чи кілька запитів домінують у загальному часі.
  • Рівні попадань у кеш скажуть, чи ви обмежені I/O через недостатній кеш або погані індекси.
  • Проблеми autovacuum / purge проявляються як зростаючі таблиці/індекси, збільшений I/O і «випадкова повільність».

Порядок важливий, бо найшвидше виправлення часто поза базою: зменшіть конкурентність клієнтів, зупиніть повтори або обмежте фонові задачі.

12+ практичних завдань з командами, значенням виводу, рішеннями

Ось перевірки, які я запускаю на 8GB VPS, перш ніж чіпати параметри налаштувань. Кожна включає: команду, що означає вивід, і яке рішення вона породжує.

Завдання 1: Підтвердити реальну пам’ять і тиск свопу

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       5.9Gi       220Mi       120Mi       1.6Gi       1.3Gi
Swap:          2.0Gi       1.1Gi       900Mi

Значення: «available» — це те, що ще можна використати без свопінгу. Своп уже в користуванні — попередження; активний рух у swap — справжня тривога.
Якщо використання swap росте під навантаженням, БД буде застрягати.

Рішення: Якщо своп суттєвий під навантаженням, зменшіть кількість з’єднань, знизьте пам’ять на сесію або додайте RAM перш ніж «оптимізувати запити».

Завдання 2: Перевірити активний своп (повільна каша)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  1 118000 210000  65000 980000  120  240  3100  4200  900 2100 18  6 32 44  0
 3  2 119200 190000  64000 970000  300  500  2800  3900 1000 2500 20  7 25 48  0

Значення: si/so вище ~0 стійко означають, що ви свопите. wa високе означає, що CPU чекає на I/O.

Рішення: Зупиніть кровотечу: обмежте конкурентність (пул), зменшіть множники пам’яті на DB, відключіть дорогі звіти в пікові години або перейдіть на швидше сховище.

Завдання 3: Виміряти латентність диска та чергування

cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0 (server)  12/31/2025  _x86_64_  (2 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.20    0.00    6.50   28.10    0.80   50.40

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await
vda              85.0   120.0  4200.0  9800.0     0.0     2.0   96.0   18.5

Значення: %util близько 100% з await у двозначних числах означає, що диск насичений.
На багатьох VPS-платформах це і є ваш вузький горлечко.

Рішення: Усуньте I/O: зменшіть write amplification (індекси, налаштування autovacuum/purge), перемістіть логи на швидше сховище або підвищіть рівень диска. Не підвищуйте кеші сліпо.

Завдання 4: Перевірити простір файлової системи та інодний тиск

cr0x@server:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        80G   62G   15G  81% /
tmpfs           3.9G     0  3.9G   0% /dev/shm
cr0x@server:~$ df -i
Filesystem       Inodes  IUsed   IFree IUse% Mounted on
/dev/vda1      5242880 220000 5022880    5% /

Значення: Майже повні диски шкодять продуктивності БД і можуть жорстко зупинити записи (WAL/redo/binlog не зможуть зрости).

Рішення: Тримайте простір вільним (я рекомендую 20%+ на зайнятих системах). Якщо ви прижато — ви на межі «неочікуваного режиму тільки для читання».

Завдання 5: Перевірити ліміти дескрипторів файлів (тихий вбивця з’єднань)

cr0x@server:~$ ulimit -n
1024

Значення: 1024 — замало для бази даних, яка може відкривати багато файлів (таблиці, індекси, WAL-сегменти) плюс сокети.

Рішення: Підніміть ліміти через systemd unit або limits.conf. Якщо БД вдариться в FD limit, клієнти бачать помилки підключення, що виглядають «випадково».

Завдання 6 (PostgreSQL): Порахувати з’єднання і побачити, хто їх скуповує

cr0x@server:~$ sudo -u postgres psql -X -c "SELECT state, count(*) FROM pg_stat_activity GROUP BY 1 ORDER BY 2 DESC;"
  state  | count
---------+-------
 idle    |   140
 active  |    12
         |     3
(3 rows)

Значення: Багато idle з’єднань зазвичай означає, що додаток скуповує їх. Це накладні витрати пам’яті і процесів, що нічого не роблять.

Рішення: Використовуйте пулер (PgBouncer) і зменшуйте прямі підключення. Встановіть розумні розміри пулів у додатку, не дозволяйте кожному поду відкривати 50 сесій «бо так за замовчуванням».

Завдання 7 (PostgreSQL): Знайти заблоковані запити та ланцюги блокувань

cr0x@server:~$ sudo -u postgres psql -X -c "SELECT pid, wait_event_type, wait_event, state, left(query,80) AS q FROM pg_stat_activity WHERE wait_event IS NOT NULL ORDER BY pid;"
 pid  | wait_event_type |  wait_event   |  state  | q
------+-----------------+---------------+---------+----------------------------------------------
 2314 | Lock            | relation      | active  | UPDATE orders SET status='paid' WHERE id=$1
 2451 | Lock            | transactionid | active  | DELETE FROM carts WHERE user_id=$1
(2 rows)

Значення: Очікування на блокування означає, що ваша конкурентність бореться сама з собою. Додавання з’єднань робить ситуацію гіршою.

Рішення: Знайдіть транзакцію-блокувальник (часто довга або idle-in-transaction). Виправте обсяг транзакцій у додатку; додайте індекси, щоб зменшити час блокування; плануйте міграції безпечно.

Завдання 8 (PostgreSQL): Перевірити ефективність кешу (ви I/O-забиті?)

cr0x@server:~$ sudo -u postgres psql -X -c "SELECT datname, blks_hit, blks_read, round(100.0*blks_hit/(blks_hit+blks_read+1),2) AS hit_pct FROM pg_stat_database ORDER BY hit_pct ASC;"
  datname  | blks_hit | blks_read | hit_pct
-----------+----------+-----------+---------
 appdb     | 89213321 |   5120032 |   94.55
 postgres  |  1200032 |     12033 |   98.99
(2 rows)

Значення: 94–95% може бути прийнятно для деяких навантажень, погано для інших. Якщо hit% падає під навантаженням і затримка диска зростає — ви страждаєте від нестачі кешу або робочий набір виріс.

Рішення: Додайте RAM або зменшіть робочий набір (кращі індекси, менше повних сканів). Не чавіть OS cache, піднімаючи shared_buffers до 6GB.

Завдання 9 (PostgreSQL): Перевірити тиск на vacuum (податок на bloat)

cr0x@server:~$ sudo -u postgres psql -X -c "SELECT relname, n_dead_tup, n_live_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 5;"
   relname   | n_dead_tup | n_live_tup |     last_autovacuum
-------------+------------+------------+----------------------------
 events      |    8200032 |   41000211 | 2025-12-31 10:22:14+00
 sessions    |    1200033 |    9000123 | 2025-12-31 10:18:01+00
 carts       |     450012 |    1500032 | 2025-12-31 09:59:44+00
(3 rows)

Значення: Накопичення dead tuples означає більше читань з диска, більші індекси, повільніші запити. Autovacuum може бути недодостатній або блокований довгими транзакціями.

Рішення: Усуньте довгі транзакції, налаштуйте autovacuum для конкретних таблиць і розгляньте партиціюцію для інтенсивних append-таблиць.

Завдання 10 (MariaDB): Перевірити поточні з’єднання і максимум використання

cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW VARIABLES LIKE 'max_connections';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 210   |
+-------------------+-------+
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 398   |
+----------------------+-------+
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 500   |
+-----------------+-------+

Значення: Ви вже досягали 398 з’єднань. Це не «ємність», це «ви фліртуєте з пам’ятним колапсом».

Рішення: Впровадьте пулювання на боці додатка або використайте MariaDB thread pool; зменшіть max_connections до безпечного числа; встановіть таймаути і вбивайте довгих сплячих.

Завдання 11 (MariaDB): Виявити сплячі з’єднання і довгі запуски

cr0x@server:~$ sudo mariadb -e "SHOW PROCESSLIST;"
Id	User	Host	db	Command	Time	State	Info
1203	app	10.0.1.12:44218	appdb	Sleep	512		NULL
1210	app	10.0.1.12:44276	appdb	Query	9	Sending data	SELECT * FROM events WHERE user_id=?
1311	app	10.0.1.13:51022	appdb	Sleep	611		NULL

Значення: Багато Sleep з великим Time — це скуповування з’єднань. Стан Sending data часто означає сканування і повернення рядків (або очікування на диск).

Рішення: Зменшіть розміри пулів у додатку, додайте обмеження тривалості життя з’єднання, увімкніть лог повільних запитів і виправте відсутні індекси.

Завдання 12 (MariaDB): Перевірити розмір і навантаження InnoDB buffer pool

cr0x@server:~$ sudo mariadb -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 4294967296 |
+-------------------------+------------+
+--------------------------+---------+
| Variable_name            | Value   |
+--------------------------+---------+
| Innodb_buffer_pool_reads | 8200332 |
+--------------------------+---------+
+----------------------------------+------------+
| Variable_name                    | Value      |
+----------------------------------+------------+
| Innodb_buffer_pool_read_requests | 932003312  |
+----------------------------------+------------+

Значення: Відношення reads from disk до read requests підказує про промахи кешу. На 8GB VPS 4GB buffer pool може бути розумним, але не якщо буфери на потік великі.

Рішення: Тримайте buffer pool достатнім (зазвичай 50–70% RAM якщо БД працює сама), але насамперед перевірте буфери на з’єднання і кількість з’єднань.

Завдання 13 (MariaDB): Виявити розливи тимчасових таблиць (таємний дискобій)

cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Created_tmp_disk_tables | 2200033  |
| Created_tmp_tables      | 3100044  |
+-------------------------+----------+

Значення: Високий відсоток тимчасових таблиць на диску вказує на запити, що сортують/групують великі набори без належних індексів, або на надто малі ліміти tmp tables.

Рішення: Виправляйте запити/індекси перш за все. Лише потім розглядайте підняття tmp_table_size/max_heap_table_size, бо їх збільшення підвищує ризик пам’яті на сесію.

Завдання 14: Підтвердити backlog сокетів і схожість на SYN-флуд

cr0x@server:~$ ss -s
Total: 1180
TCP:   942 (estab 410, closed 410, orphaned 0, timewait 110)

Transport Total     IP        IPv6
RAW       0         0         0
UDP       8         6         2
TCP       532       410       122
INET      540       416       124
FRAG      0         0         0

Значення: Якщо встановлених з’єднань багато і timewait швидко зростає, ваш додаток може агресивно перепідключатися або балансувальник погано налаштований.

Рішення: Виправляйте поведінку клієнта (pool, keepalive, таймаути). Бази ненавидять churn з’єднань майже так само, як люди ненавидять несподівані виклики по черзі.

Завдання 15: Підтвердити steal CPU (реальність VPS)

cr0x@server:~$ mpstat 1 3
Linux 6.8.0 (server)  12/31/2025  _x86_64_  (2 CPU)

12:00:01 AM  all   %usr  %nice  %sys %iowait  %irq  %soft  %steal  %idle
12:00:02 AM  all  22.00   0.00  10.00   18.00  0.00   1.00    6.00  43.00
12:00:03 AM  all  20.00   0.00   9.00   19.00  0.00   1.00    7.00  44.00

Значення: %steal показує, що гіпервізор відбирає CPU. Якщо steal зростає під час піків, ваша «повільність» — це буквально чужа робота.

Рішення: Розгляньте кращий клас VPS або виділені CPU. Налаштування не виправить вкрадені цикли.

Безпечні шаблони масштабування для більшої кількості клієнтів

Шаблон 1: Пулювання з’єднань — не опція на 8GB

Якщо ви запускаєте PostgreSQL і очікуєте багато одночасних клієнтів — обов’язково поставте пулер. Точка.
Навіть якщо у вас «лише» 100 інстансів додатку. Особливо тоді.

Для PostgreSQL зовнішнє пулювання (PgBouncer) — стандартний крок, бо воно зменшує кількість бекенд-процесів, зберігаючи клієнтську конкуренцію.
Для MariaDB багато стеків використовують пулювання на рівні додатка; MariaDB thread pool може допомогти, але це не безкоштовний обід.

Безпечна ментальна модель: клієнтів може бути багато, а конкуренція сервера повинна бути малою і контрольованою.
Ви хочете чергу, яку контролюєте ви, а не ресурсний шторм, який контролює ядро.

Шаблон 2: Встановіть жорсткі ліміти і дотримуйтесь їх

«Необмежено» — шлях до каскадної відмови. Обмежуйте:

  • Підключення до бази даних (max_connections / max_connections).
  • Підключення по користувачу або додатку (ролі, користувачі, правила фаєрволу, налаштування пулера).
  • Час виконання запиту (PostgreSQL statement_timeout, MariaDB max_execution_time для SELECT, плюс таймаути на боці додатка).
  • Idle-in-transaction (PostgreSQL idle_in_transaction_session_timeout — справжній порятунок).

Шаблон 3: Захищайте кеш, тримайте множники пам’яті невеликими

Найпоширеніша помилка налаштування на 8GB — витратити всю RAM на «пам’ять бази даних», забувши:
буфери на сесію множаться, а кеш ОС — не «витрата пам’яті».

PostgreSQL: якщо ви ставите work_mem великим і дозволяєте багато активних сесій, можете виділити набагато більше пам’яті, ніж маєте.
MariaDB: якщо надути sort/join буфери і дозволити багато потоків, отримаєте те саме, але з менш очевидною звітністю.

Шаблон 4: Зробіть записи нудними

На маленьких VPS-дисках записи спричиняють більшість проблем:
fsync WAL/redo, чекпоінти, бекграундний flush, тимчасові таблиці, vacuum, підтримка індексів.
Ваша мета — рівномірна поведінка записів, а не «швидко до того, як відмовить».

  • Тримайте транзакції короткими.
  • Пакуйте фонові роботи у непіковий час або обмежуйте їх.
  • Не створюйте три нові індекси опівдні на зайнятій таблиці.
  • Встановлюйте розумну надійність; не вимикайте fsync, якщо ви не готові випробувати втрату даних.

Шаблон 5: Масштабуйте читання і запис окремо

На одному 8GB VPS вертикальне масштабування обмежене. Тож масштабування — це скоріше зміна форми:

  • Читання: кешування, репліки для читання, оптимізація запитів, індексація, денормалізовані моделі читання там, де потрібно.
  • Записи: зменшення write amplification, партиціювання «шумних» таблиць, архівація старих даних, уникнення «оновлення кожного рядка».

PostgreSQL і MariaDB обидва можуть реплікувати. Операційні компроміси різняться, але принцип однаковий: не чекайте, що одна коробка витягне нескінченну роботу.

Шаблон 6: Спостерігайте за базою ззовні

Внутрішні метрики — чудо. Зовнішні симптоми краще для триажу:

  • p95/p99 затримки запитів
  • disk await і utilization
  • активність свопу
  • швидкість підключень (нових з’єднань/сек)
  • поведінка чекпоінтів/flush

Якщо ви цього не бачите, ви «вирішуватимете» не ту проблему. Саме так інциденти повторюються з кращими дашбордами.

Жарт №2: Вимкнути надійність, щоб «підвищити продуктивність», — це як зняти гальма, щоб виграти перегони. Ви приїдете швидше, але недовго.

Три міні-історії з корпоративного життя

Міні-історія 1: Інцидент через неправильне припущення (PostgreSQL з’єднання)

Середня SaaS-команда перейшла від керованої бази до самохостованого PostgreSQL на 8GB VPS, щоб зекономити.
Міграція пройшла гладко. Перший тиждень був тихим. Всі похвалилися і повернулись до фіч.

Потім вони підключили великого клієнта. Трафік подвоївся. Затримка повільно зростала, але тільки у робочі години. Команда вирішила, що це «просто важчі запити»
і почала ганяти індекси. Трохи допомогло. Потім стався реальний інцидент: додаток почав видавати періодичні 500, а графік CPU БД дивно спокійний.

Неправильне припущення було тонким: вони думали «PostgreSQL витримає багато з’єднань як наша стара система».
Рівень пулу додатка був за замовчуванням 50 на інстанс. З кількома інстансами це вже багато. Автоскейл підняв це до вибуху.
Коробка запустила сотні бекенд-процесів, з’їла пам’ять, змусила ОС звільняти кеш, і перетворила кожен промах кешу на I/O wait-party.

Виправлення було нудним: зменшили розміри пулів додатка, розгорнули PgBouncer, обмежили max connections на сервері і поставили таймаути, щоб idle сесії не тягнули систему.
Вони також відрегулювали поведінку повторів, щоб уникнути петлі зворотного зв’язку.

Після цього тонке налаштування запитів почало працювати — бо система більше не вбивала себе сама.
Урок закріпився: на малих машинах управління з’єднаннями — це управління продуктивністю.

Міні-історія 2: Оптимізація, що відкотилася (налаштування пам’яті MariaDB)

Інша компанія тримала MariaDB для внутрішньої аналітики на 8GB VM. Переважно читання, періодичні пакетні завантаження.
Хтось прочитав старий гай і вирішив, що «більші буфери завжди краще». Збільшили per-thread sort та join буфери і підняли ліміти тимчасових таблиць.

На спокійній системі все було чудово. Запити, що раніше скидалися на диск, стали швидшими. Дашборд відчувався шустріше.
Зміни схвалили в продакшн без стрес-тестів, бо система «не була критичною». Це фраза, що погано старіє.

Коли наступне пакетне завантаження запустилося, воно підняло кілька звітів і ETL-конвеєрів. Кожна робота відкривала кілька з’єднань.
Тепер кожне з’єднання могло алокувати великі буфери. Пам’ять різко зросла, ядро запалило своп, і коробка увійшла в класичний стан:
CPU здебільшого простає, load average високий, запити таймаутяться, і всі звинувачують «повільну базу».

Відкат налаштувань пам’яті стабілізував систему. Довгострокове рішення — дисципліна:
поставити ліміт на одночасні важкі завдання, виправити найгірші запити й індекси, і тримати per-connection пам’ять малою, щоб конкуренція не перетворювалася на хаос.

Висновок: налаштування пам’яті можуть бути чи то ключем до продуктивності, чи то кнопкою самознищення. На 8GB вони зазвичай обоє.

Міні-історія 3: Нудна, але правильна практика, що врятувала день (голова дискового простору та гігієна WAL/binlog)

Одна команда працювала з міксом транзакцій і фонових завдань на одному VPS, чекаючи бюджету на покращення.
Не ідеально, але реальність має свої вимоги. Вони робили одну річ постійно: рутинні перевірки ємності і консервативний запас дискового простору.

Вони відстежували використання диска, патерни росту WAL/binlog і стежили за тим, щоб ротація логів була правильно налаштована.
Також вони моніторили лаг реплікації (якщо ввімкнено) і тримали жорсткі алерти по заповненню файлової системи.
Нічого з цього не було гламурним. Ніхто за це не отримав підвищення.

Одного дня деплой приніс баг, який генерував значно більше записів, ніж зазвичай. WAL/binlog почали рости.
На багатьох командах це момент, коли ви дізнаєтеся, що диск повний, і база перестає приймати записи.
У них цього не сталося. Алерт спрацював раніше, ніж вони опинилися в зоні ризику.

На чергуванні обмежили проблемну роботу, призупинили реліз і зменшили радіус вибуху, поки БД залишалась здоровою.
Постмортем був коротким. Виправлення просте. Вплив на клієнтів мінімальний.

Нудні практики не запобігають кожному багу. Вони не дають багам перерости в інциденти.

Типові помилки: симптом → корінна причина → виправлення

1) «БД повільна», але CPU низький і load average високий

Симптом: p95 затримки стрибає; CPU простоїть; load високий; користувачі скаржаться.

Корінна причина: I/O wait (насичення диска) або свопінг.

Виправлення: Перевірте iostat/vmstat. Зменшіть конкуренцію, зупиніть своп, перейдіть на кращий диск, і зменшіть write amplification (індекси, tuning vacuum/purge).

2) Випадкові помилки підключення під час піків трафіку

Симптом: «too many connections», «could not connect», переривчасті відмови.

Корінна причина: Небезпечні пула додатків + відсутність пулювання + низькі FD-ліміти.

Виправлення: Впровадьте ліміти пулів, використайте PgBouncer для PostgreSQL, встановіть реалістичний max_connections, підніміть ulimit -n і додайте backpressure.

3) PostgreSQL гіршає протягом днів/тижнів, а потім «чарівно» покращується після обслуговування

Симптом: Поступове уповільнення; диск росте; більше читань; з’являються логи autovacuum.

Корінна причина: Bloat таблиць/індексів від MVCC і недостатнього vacuum, часто погіршений довгими транзакціями.

Виправлення: Знайдіть і усуньте довгі/idle-in-transaction сесії. Налаштуйте autovacuum для таблиць. Плануйте vacuum/analyze там, де потрібно.

4) MariaDB нормальна, поки не запустять один звіт, потім все стає застиглим

Симптом: Один дашборд/звіт викликає глобальне уповільнення; spike тимчасових таблиць.

Корінна причина: Поганий план запиту + відсутній індекс + тимчасові таблиці, що спадають на диск + конкуренція.

Виправлення: Використовуйте лог повільних запитів, EXPLAIN, додавайте індекси, обмежуйте одночасні звітні роботи і не надувайте per-thread буфери як тимчасове рішення.

5) Реплікація відстає в пікові години

Симптом: Репліка для читання відстає; ризик фейловера зростає.

Корінна причина: Пики записів, повільний fsync диска, великі транзакції або тиск чекпоінтів/flush.

Виправлення: Зменшуйте піки записів, розбивайте великі транзакції, налаштовуйте поведінку чекпоінтів/flush обережно і переконайтесь, що налаштування реплікації відповідають можливостям диска.

6) Затримки стрибають кожні кілька хвилин, як серцебиття

Симптом: Регулярні спади, періодичні уповільнення, потім відновлення.

Корінна причина: Чекпоінти/flush storms (чекпоінти PostgreSQL; InnoDB flushing) або зіткнення запланованих робіт.

Виправлення: Згладьте write навантаження; налаштуйте інтервал чекпоінтів і completion_target (PostgreSQL); перевірте InnoDB flushing; плануйте пакетні роботи поза піком.

7) «Ми підняли кеші і стало гірше»

Симптом: Більш високі налаштування пам’яті ведуть до зростання затримок, а не зменшення.

Корінна причина: Звуження OS cache, підвищений ризик swap або збільшення запасу для per-session алокацій.

Виправлення: Відкотіться. Тримайте запас. Робіть налаштування пам’яті пропорційними до конкуренції та навантаження, а не до надії.

Контрольні списки / покроковий план

Покроково: як безпечно масштабувати клієнтів на 8GB VPS (будь-яка база)

  1. Розділіть обов’язки: якщо можливо, тримайте БД на окремому VPS. Якщо ні — бюджетуйте пам’ять і CPU для БД явно.
  2. Виміряйте базову лінію: disk await, активність свопу, кількість з’єднань, p95 затримку. Запишіть їх. Якщо не запишете — потім будете сперечатися.
  3. Встановіть жорсткі ліміти з’єднань: обмежте серверні з’єднання і розміри пулів додатка. Уникайте «автоскейл = нескінченні з’єднання».
  4. Додайте пулювання: PgBouncer для PostgreSQL; для MariaDB — надійне пулювання на рівні додатка і розгляньте thread pool.
  5. Налаштуйте таймаути: вбивайте idle-in-transaction; встановіть таймаути запитів для runaway-запитів; використовуйте таймаути додатка, що не створюють штормів повторів.
  6. Захистіть RAM: тримайте per-session пам’ять малою; резервуйте кеш ОС; уникайте свопу. Додайте запас пам’яті і дотримуйтеся його.
  7. Увімкніть видимість повільних запитів: slow query log (MariaDB) або pg_stat_statements (PostgreSQL) і лог повільних запитів.
  8. Виправте найгірших першими: додавайте відсутні індекси, ліквідуйте N+1, усуньте великі незконтрольовані скани.
  9. Стабілізуйте записи: уникайте величезних транзакцій; пакуйте фонові роботи; обережно налаштовуйте чекпоінти/flush та перевіряйте метрики.
  10. Плануйте зростання: якщо кількість клієнтів зростає, плануйте репліки для читання, партиціювання або перехід на більшу машину до того, як інцидент змусить це зробити.

Контрольний список для PostgreSQL (8GB VPS)

  • Використовуйте пулер, якщо конкуренція клієнтів більше кількох десятків.
  • Тримайте shared_buffers помірним (часто близько 1–2GB на 8GB, якщо БД працює сама); залиште місце для кешу ОС.
  • Встановіть work_mem консервативно і пам’ятайте, що воно може використовуватися кілька разів на план запиту.
  • Стежте за autovacuum: dead tuples, довгі транзакції, ризик freeze.
  • Перевіряйте події очікування перед зміною налаштувань. Блокування і I/O очікування вимагають різних рішень.

Контрольний список для MariaDB (8GB VPS)

  • Реалістично підберіть innodb_buffer_pool_size (часто 4–6GB на DB-only 8GB, залежно від кількості з’єднань і буферів на потік).
  • Тримайте per-thread буфери розумними; уникайте «просто підняти sort buffer» без розрахунку конкуренції.
  • Увімкніть і переглядайте slow query log регулярно; виправляйте найгірші запити, не середні.
  • Перевіряйте поведінку тимчасових таблиць і зменшуйте їх кількість на диску індексами і переписуванням запитів.
  • Розгляньте thread pool, коли у вас багато підключень, але обмежений CPU.

Правило планування потужності (практичне, не досконале)

На 8GB прагніть до малої, стабільної кількості активних виконуваних запитів (десятки, не сотні).
Дозвольте клієнтам чергуватися в пулері або на боці додатку, замість того щоб ядро і база змагалися за пам’ять і I/O.

FAQ

1) Що «краще» на 8GB VPS: MariaDB чи PostgreSQL?

Якщо вам потрібна сувора коректність семантики, сильна видимість очікувань/блокувань і передбачувані інструменти планування запитів — PostgreSQL зазвичай дає кращий досвід для операцій.
Якщо ви глибоко в екосистемі MySQL і ваше навантаження просте OLTP з дисциплінованим пулюванням — MariaDB теж може працювати дуже добре.
Більший предиктор — чи контролюєте ви кількість підключень і поведінку записів.

2) Скільки з’єднань мені дозволяти?

Менше, ніж ви думаєте. На 8GB VPS прямі підключення до бази у сотнях — звичний шлях до свопу і I/O-колапсу.
Віддавайте перевагу невеликому серверному ліміту (зазвичай десятки або низькі сотні залежно від навантаження) і чергуйте на рівні пулера/додатка.
Вимірюйте пам’ять і затримку в піку, потім регулюйте.

3) Чи справді мені потрібен PgBouncer для PostgreSQL?

Якщо у вас багато інстансів додатка, короткоживучі підключення або спікований трафік — так.
Модель підключень PostgreSQL робить пулювання чистим способом масштабувати кількість клієнтів без масштабування бекенд-процесів.
Якщо у вас мало довгоживучих підключень і немає стрибків — можна обійтися без нього, поки не стане потрібно.

4) Чи може MariaDB витримати більше з’єднань, ніж PostgreSQL без пулювання?

Вона може обробити більше потоків, ніж PostgreSQL процесів, але «витримати» не означає «залишатися швидкою».
Пам’ять на з’єднання і буфери на запит все ще множаться, і контенція зростає.
Пулювання і розумна поведінка додатка все ще важливі.

5) Чи варто збільшувати shared_buffers (PostgreSQL) до більшої частини RAM?

Ні. PostgreSQL сильно виграє від кешу ОС. На 8GB помірний shared_buffers часто показує кращу продуктивність, бо лишає місце для файлового кешу і уникає пам’ятного тиску. Перевіряйте по hit rates і disk await.

6) Чи ставити innodb_buffer_pool_size на 75–80% RAM (MariaDB)?

Лише якщо БД — основний сервіс і кількість підключень та per-thread буфери контролюються.
Якщо у вас висока конкурентність або великі буфери на сесію, 80% може штовхнути вас у своп під час піків.
Починайте консервативно, вимірюйте, потім збільшуйте.

7) Чому продуктивність погіршується, коли я збільшую пам’ять на запит (work_mem, sort/join buffers)?

Тому що конкурентність перетворює «на запит» у «на все».
Вища пам’ять на запит може допомогти окремому запиту, але під навантаженням вона збільшує загальний об’єм пам’яті, стискає кеш і викликає своп.
На малій RAM вам потрібна передбачувана поведінка більше, ніж пікова швидкість окремого запиту.

8) Який перший сигнал, що я I/O-забитий?

Зростаюча затримка при низькому CPU і високому iowait — класика. iostat показує високе %util і підвищений await.
Події очікування PostgreSQL часто показують I/O-залежні очікування; MariaDB може показувати «Sending data», насправді чекаючи читання.

9) Чи безпечно відключити fsync або послабити надійність, щоб пережити пікове навантаження?

Це бізнес-рішення у вигляді налаштування. Якщо ви відключаєте fsync — ви погоджуєтесь на втрату даних при краху.
Якщо вам потрібна пропускна здатність, спочатку виправте конкуренцію, запити і сховище. Послаблювати надійність варто лише якщо ви можете без побоювання пояснити радіус ураження зацікавленим сторонам.

10) Коли припинити налаштування і апгрейдити VPS?

Коли диск насичений у стабільному стані, коли неможливо уникнути свопу під час піку попри дисципліну, або коли %steal CPU домінує.
Налаштування не створить IOPS і не поверне вкрадені цикли. Іноді апгрейд — найнадійніша оптимізація.

Наступні кроки, які можна зробити цього тижня

  1. Виберіть стратегію підключень: пулер для PostgreSQL, жорстке пулювання додатка для MariaDB. Запишіть цільовий max server concurrency.
  2. Запустіть швидкі перевірки під час піку: vmstat, iostat, лічильники з’єднань, очікування блокувань.
  3. Увімкніть видимість повільних запитів і виправте топ-3 винуватців. Не 30. Три.
  4. Встановіть таймаути, що запобігають неконтрольованим сесіям і штормам повторів.
  5. Створіть бюджет ємності для RAM: спільний кеш + накладні витрати на підключення + запас для кешу ОС. Потім забезпечте його дотримання лімітами.
  6. План виходу: якщо зростання триває, вирішіть заздалегідь, чи додаватимете репліку, переходите на більшу коробку або розділятимете навантаження.

На 8GB VPS ви не виграєте завдяки хитрості. Ви виграєте дисципліною: менше з’єднань, стабільніші записи та відмова від того, щоб значення за замовчуванням визначали архітектуру.

← Попередня
Термопрокладки: виправлення за $10, яке може змінити всю відеокарту
Наступна →
Відображення ACL ZFS для SMB: як уникнути кошмарів з правами

Залишити коментар