MariaDB проти SQLite для маленьких VPS-проєктів: коли MariaDB зайва

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

Ви орендували маленький VPS, бо дієте відповідально. Потім встановили MariaDB, бо хочете більше можливостей. Тепер у вашого «простого застосунку» є демо́н бази даних, буферний пул, система автентифікації, хвилі підключень і історія резервного копіювання, яку ви ще не перевіряли. Тим часом ваш робочий навантаження виглядає так: «кілька користувачів, cron-завдання та один веб-процес, що переважно читає».

Це момент визнати нудну правду: для великої частини маленьких VPS-проєктів MariaDB — це не вибір бази даних. Це вибір операційної моделі. SQLite часто є кращим дефолтом — доки це не перестає відповідати вимогам.

Рішення, яке ви насправді приймаєте

Люди формулюють це як «SQLite проти MariaDB». Це упущення істини. Справжнє рішення таке:

  • Вбудований файл бази даних (SQLite): менше рухомих частин, менше фонових потоків, менше налаштувань, менше режимів відмови. Ви відмовляєтеся від деяких патернів конкурентності та деяких операційних інструментів.
  • Клієнт-серверна служба бази даних (MariaDB): мережевий протокол, аутентифікація, пуулінг підключень, фонове обслуговування, опції реплікації та безліч речей, які ви можете неправильно налаштувати о 2 ночі.

На маленькому VPS (1–2 vCPU, 1–4 ГБ RAM, скромний SSD) обмежуючим фактором рідко є «набір SQL-функцій». Зазвичай це одне з:

  • тиск на оперативну пам’ять і свапінг (тихий вбивця продуктивності)
  • стрибки затримки вводу/виводу (шумні сусіди, кредитні ліміти на бурсти, дешеве сховище)
  • занадто багато підключень або потоків для CPU
  • резервні копії, що існують лише у вашій уяві
  • патерн конкурентності, який виглядає нормально в розробці, але розвалюється при невеликому реальному навантаженні

Якщо ваш застосунок однузловий, з низьким або помірним обсягом записів і ви волієте випустити продукт, а не доглядати демон, SQLite заслуговує першого розгляду. Якщо вам потрібно багато одночасних записів, віддалений доступ із кількох машин або онлайн-міграції без простоїв, MariaDB починає виглядати не як зайва надмірність, а як «дорослий наглядач».

Перефразована ідея від Werner Vogels (підхід інженерії/надійності): «Усе ламається, тому проектуйте, очікуючи відмови». Будуйте свій шар даних навколо цього, а не навколо відчуттів.

Жарт №1: Запуск MariaDB для маленького хобі-застосунку — це як найняти штатного бухгалтера для управління дрібними витратами: вражає, але паперова тяганина переможе.

Факти та історія, що важливі в продакшені

Трохи контексту — це не дрібниці; він пояснює, чому ці системи поводяться так, як поводяться.

  1. SQLite вбудований за дизайном. Це бібліотека, підключена до вашого процесу, а не демон. Ось чому вартість «підключення» мізерна і чому права на файл раптово набувають великого значення.
  2. SQLite широко використовується в продакшені з 2000 року. Це не «іграшкова технологія». Його використовують у браузерах, телефонах і численних вбудованих системах, бо він стабільний і нудний.
  3. WAL-процес в SQLite змінив правила гри. Write-Ahead Logging покращив конкурентність для багатьох робочих навантажень, орієнтованих на читання, дозволяючи читачам під час записів (з певними обмеженнями).
  4. MySQL з’явився першим; MariaDB — форк. MariaDB відколовся після придбання Sun компанією Oracle. Історія форку важлива, коли ви читаєте старі поради «MySQL» і припускаєте, що вони застосовні без змін.
  5. InnoDB став дефолтним сховищним двигуном. Транзакційна семантика, відновлення після краху та блокування на рівні рядка — ось чому він домінує. Саме тому розмір пам’яті та поведінка fsync мають значення.
  6. Модель блокувань SQLite орієнтована на файл. Цей один файл — і чарівність, і обмеження. Просто доти, поки ваш робочий навантаження не має багато записувачів або довгих транзакцій.
  7. MariaDB має пул потоків та кілька шляхів виконання. Налаштування обробки потоків може зробити продуктивність кращою або гіршою на малих CPU; дефолтні налаштування можуть бути або прийнятними, або катастрофічними залежно від поведінки підключень.
  8. «Типізовані» колонки в SQLite гнучкіші, ніж здається. Афінітет типу — не те саме, що суворе типування; це може бути або корисною особливістю, або пасткою залежно від гігієни даних.
  9. MariaDB приносить зрілі інструменти. Журнали повільних запитів, альтернативи performance schema та опції реплікації — реальні переваги, коли вам потрібна спостережуваність і масштабування.

Форми навантаження: хто перемагає де

Випадок A: один VPS, один екземпляр застосунку, переважно читання

Обирайте SQLite, якщо немає конкретної причини інакше. Ви отримуєте:

  • немає мережевого хопу
  • немає потреби в пуулінгу підключень (але все одно потрібне розумне використання на рівні застосунку)
  • один файл, який можна робити бекап, контролювати контрольну суму та переносити
  • нижчий слід пам’яті; немає буферного пулу, який треба налаштовувати

З режимом WAL і короткими транзакціями SQLite може витримати несподівано великий трафік. Пастка — довгі транзакції та звичка «записувати для кожного запиту».

Випадок B: багато одночасних записувачів (черги, чатливий API, метрики)

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

Випадок C: кілька серверів додатків або окремий job runner

Якщо кілька машин мають записувати в ту саму базу, SQLite зазвичай — невірний інструмент. Так, можна помістити SQLite на мережеве сховище. Ні, зазвичай не варто. Момент, коли ви вводите мережеві файлові системи в архітектуру «маленького VPS», ви створюєте симулятор відмов.

Випадок D: потрібні онлайн-міграції та операційні важелі

MariaDB дає більше важелів: онлайн DDL у багатьох випадках, краща інструментованість під час виконання та відпрацьовані патерни міграцій/реплікації. SQLite може робити міграції, але не дає тієї самої операційної гнучкості, коли ви в складнощах.

Випадок E: дані малі й цінні

Один файл SQLite привабливий для «цінних маленьких» даних: конфігураційний стан, токени білінгу, feature flags, аудиторські логи для невеликого застосунку, кеші, про які ви дбаєте. Ваша історія бекапів та відновлення може бути надзвичайно простою — якщо робити це правильно.

Коли MariaDB — зайва (і чому це болісно)

MariaDB не «важка» у тому сенсі, як це мають на увазі люди з ентерпрайз-команд. Вона важка у тому сенсі, як відчувають маленькі сервери: фоновий сервіс із апетитом до пам’яті та великою кількістю потоків, які не зменшуються чемно.

Прихований податок: пам’ять і свап

На маленьких VPS найпоширенішою причиною відмов MariaDB не є «помилковий індекс». Це свап-трэш. Коли MariaDB + застосунок + OS page cache перевищують RAM, система деградує в повільне падіння. Затримки ростуть нелінійно. Запити шикуються в чергу. Таймаути викликають повторні спроби. Навантаження зростає. Ви спостерігаєте, як воно горить.

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

Підключення, потоки та «це ж просто VPS»

Малі застосунки часто використовують дефолтний розмір пула ORM 10–50 на процес, бо «так написано в блозі». На маленькому VPS це може означати:

  • занадто багато потоків бази даних
  • накладні витрати на переключення контексту
  • посилене блокування через конкуренцію
  • пам’ять на підключення

SQLite переважно уникає «штормів підключень», бо підключення — внутрішньопроцесні і дешеві. Ви все ще можете «вистрілити собі у ногу» конкуренцією, але випадково відкрити 400 TCP-підключень до себе важче.

Операційна поверхня

MariaDB приносить привілеї, мережеву експозицію, TLS, управління користувачами, власність каталогу даних, ротацію логів, оновлення з сумісністю та фонові завдання. Жодна з цих речей не є зло. Але для маленького проєкту кожна — потенційний поглинач часу.

Тест на запах надмірності

MariaDB ймовірно зайва, якщо більшість з цього правда:

  • один VPS, один екземпляр застосунку
  • записи рідкісні (секунди або хвилини між записами, або невеликі партії)
  • набір даних комфортно вміщується в OS page cache (або просто малий)
  • вам не потрібен віддалений доступ з кількох хостів
  • вам не потрібна висока конкурентність записів
  • ви надаєте перевагу простим бекапам та відновленню

SQLite: гострі кути, яких слід уникати

Конкуренція — це про транзакції, а не про бажання

SQLite може обслуговувати багато читачів і одного записувача одночасно. Режим WAL покращує співіснування читання та запису, але не робить «багато записувачів» безкоштовними. Найгірший патерн — довгі транзакції або будь-які транзакції, що тримають блокування, поки ви робите мережевий I/O чи складну роботу у застосунку.

Правило проектування: тримайте транзакції короткими, робіть роботу поза транзакцією, потім записуйте.

Режим WAL — не опція для більшості веб-навантажень

Якщо ви будуєте веб-застосунок і залишите дефолтний режим rollback journal, ви добровільно обираєте більше болю з блокуваннями. WAL зазвичай — правильний вибір. Це також змінює семантику бекапів: треба враховувати WAL-файли.

Налаштування довговічності — реальні компроміси

SQLite дозволяє легко встановити pragma типу synchronous=NORMAL і відчути себе майстром продуктивності. Ви також відмовляєтеся від частини гарантій довговічності. Якщо ваш VPS може втратити живлення або гіпервізор може некоректно перезавантажитися (може), ви маєте знати, на що підписуєтеся.

Мережеві файлові системи — часта пастка

SQLite очікує адекватної POSIX-файлової семантики. На багатьох мережевих файлових системах блокування та поведінка fsync — це… інтерпретативний танець. Локальний SSD на VPS: добре. NFS/SMB/«яке-небудь спільне щось»: зазвичай ні.

Один файл означає один набір прав

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

MariaDB: гострі кути, з якими ви зіткнетеся

Дефолтні конфіги не «безпечні для малого»

Дефолти MariaDB часто адекватні для серверів загального призначення, але «загальне призначення» припускає більше ресурсів, ніж ваш VPS за $5. Буферний пул, буфери на підключення та фонові потоки можуть перевищити те, що ви можете дозволити собі.

Пам’ять на підключення — тихий витік RAM

Це не витік, це гірше: «працює як задумано». Кожне підключення може виділяти буфери для сортування, join-операцій, тимчасових таблиць тощо. У результаті шторми підключень перетворюються на пам’ятні шторми. Ви бачите свап, потім таймаути, потім паніку.

fsync і довговічність: продуктивність залежить від реального сховища

Довговічність InnoDB залежить від скидання журналів на диск. На дешевому VPS-сховищі затримки fsync можуть стрибати. Ви побачите періодичні паузи, і ваш застосунок звинуватить «повільну базу даних», ніби це її характерний недолік.

Резервні копії — це процес, а не файл

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

Проблеми безпеки

SQLite не слухає портів. MariaDB слухає. Якщо ви випадково виставите її в Інтернет, ви дізнаєтеся, як поширені атаки перебором паролів. «Але я використав сильний пароль» — це не стратегія; це надія.

Жарт №2: Відкрити порт 3306 у Інтернет — відмінний спосіб познайомитися з ботами, які ніколи не сплять — на відміну від вашого on-call графіка.

Практичні завдання: команди, виводи та рішення

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

Завдання 1: Чи VPS свапить?

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       1.6Gi       120Mi        32Mi       230Mi       170Mi
Swap:          1.0Gi       780Mi       244Mi

Що це означає: Свап використовується інтенсивно. На маленькому VPS це корелює з випадковими багатосекундними затримками.

Рішення: Якщо у вас MariaDB — зменшіть слід пам’яті (buffer pool, кількість підключень) або перейдіть на SQLite, якщо робоче навантаження підходить. Якщо у вас SQLite — зменшіть пам’ять застосунку або додайте RAM; SQLite не є причиною свапу, але також може страждати.

Завдання 2: Ми в пеклі I/O wait?

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 798720 122880  11264 215040    0    5   120   980  520  840 18  7 55 20  0
 1  2 798720 118432  11264 213120    0   12   210  1460  600  920 12  6 48 34  0

Що це означає: Високий wa (I/O wait) і свапування (so) вказують на затримки сховища та тиск на пам’ять.

Рішення: Спочатку виправте сховище та RAM. Не налаштовуйте SQL, поки хост тоне.

Завдання 3: MariaDB — головний споживач пам’яті?

cr0x@server:~$ ps -eo pid,comm,rss,pcpu --sort=-rss | head
 2481 mariadbd           612340  18.2
 3022 python3            248120   9.1
 1102 nginx               45200   0.3

Що це означає: RSS MariaDB ≈600MB; на системі з 2GB це може бути нормально, на 1GB — ні.

Рішення: Якщо ваш набір даних малий і конкурентність записів низька, розгляньте перехід на SQLite, щоб звільнити RAM. Інакше налаштуйте InnoDB buffer pool і використання підключень.

Завдання 4: Скільки підключень ми реально використовуємо (MariaDB)?

cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';"
Variable_name	Value
Threads_connected	87
Variable_name	Value
max_connections	151

Що це означає: Ви наближаєтеся до стелі. Кожне підключення має накладні витрати.

Рішення: Виправте розмір пула в застосунку, додайте пуулер або зменшіть конкурентність. Не просто підвищуйте max_connections на маленькому VPS, якщо не врахували RAM.

Завдання 5: Чи каже нам slow query log правду (MariaDB)?

cr0x@server:~$ sudo mariadb -e "SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';"
Variable_name	Value
slow_query_log	OFF
Variable_name	Value
long_query_time	10.000000

Що це означає: Логування вимкнено, поріг занадто великий. Ви сліпі.

Рішення: Тимчасово увімкніть slow query log з низьким порогом (наприклад 0.2–0.5с), щоб знайти реальних порушників. Потім підніміть поріг, щоб уникнути спаму в логах.

Завдання 6: CPU-bound чи I/O-bound (швидкий огляд)?

cr0x@server:~$ top -b -n 1 | head -15
top - 12:00:11 up 21 days,  2:14,  1 user,  load average: 3.12, 2.44, 1.98
Tasks: 132 total,   2 running, 130 sleeping,   0 stopped,   0 zombie
%Cpu(s): 72.0 us,  8.0 sy,  0.0 ni, 12.0 id,  8.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   2048.0 total,    140.0 free,   1550.0 used,    358.0 buff/cache

Що це означає: Високий користувацький CPU вказує на виконання запитів або логіку застосунку; непомірний I/O wait вказує на затримки диска теж.

Рішення: Якщо CPU високий при невеликій кількості запитів — перевірте плани виконання і індекси. Якщо стрибки I/O wait корелюють із комітами — перевірте поведінку fsync і стан диска.

Завдання 7: Швидка перевірка здоров’я SQLite (integrity)

cr0x@server:~$ sqlite3 /var/lib/myapp/app.db "PRAGMA integrity_check;"
ok

Що це означає: Файл бази даних структурно узгоджений.

Рішення: Якщо не «ok», зупиніть записи, зробіть копію та плануйте відновлення. Корупція рідкісна, але реальна — особливо при небезпечному сховищі або жорстких вбивствах процесів.

Завдання 8: Режим журналу і synchronous у SQLite

cr0x@server:~$ sqlite3 /var/lib/myapp/app.db "PRAGMA journal_mode; PRAGMA synchronous;"
wal
2

Що це означає: Увімкнено WAL; synchronous=2 означає FULL (надійніше, повільніше).

Рішення: Для багатьох VPS-застосунків WAL — обов’язковий. Тримайте synchronous FULL, якщо важлива коректність даних. Якщо обираєте NORMAL — робіть це свідомо і документуйте ризик.

Завдання 9: Симптоми блокування SQLite (busy timeouts)

cr0x@server:~$ sqlite3 /var/lib/myapp/app.db "PRAGMA busy_timeout;"
0

Що це означає: Немає busy timeout; при конкуренції записувачі можуть одразу падати.

Рішення: Встановіть розумний busy timeout у застосунку (або через PRAGMA на підключення) і виправте довжину транзакцій. Busy timeout — пластир; довгі транзакції — інфекція.

Завдання 10: Перевірка розміру InnoDB buffer pool (MariaDB)

cr0x@server:~$ sudo mariadb -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
Variable_name	Value
innodb_buffer_pool_size	1073741824

Що це означає: Буферний пул 1GB. На системі 1–2GB це може бути занадто багато, якщо врахувати OS, page cache і пам’ять застосунку.

Рішення: На малих VPS починайте консервативно. Якщо відбувається свап, зменшіть його. RAM, якої у вас немає, не є кешем — це генератор таймаутів.

Завдання 11: MariaDB: тимчасові таблиці на диску?

cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'; SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';"
Variable_name	Value
Created_tmp_disk_tables	1842
Variable_name	Value
Created_tmp_tables	9621

Що це означає: Значна частина тимчасових таблиць попадає на диск, що може бути повільно на бюджетному сховищі.

Рішення: Перевірте запити, що викликають сорти/joins; додайте індекси; розгляньте збільшення лімітів тимчасових таблиць лише якщо є запас RAM (рідко на маленьких VPS).

Завдання 12: MariaDB: швидкий знімок найповільніших запитів (processlist)

cr0x@server:~$ sudo mariadb -e "SHOW FULL PROCESSLIST;"
Id	User	Host	db	Command	Time	State	Info
412	app	user@localhost	mydb	Query	12	Sending data	SELECT * FROM events WHERE user_id=...
431	app	user@localhost	mydb	Sleep	55		NULL

Що це означає: Один запит працює 12 секунд; багато сплячих підключень можуть вказувати на проблеми з пулом.

Рішення: Знайдіть цей запит, зробіть EXPLAIN, додайте індекс або перепишіть. Для сплячих підключень — зменшіть розмір пулу або час життя; припиніть накопичувати ідл-підключення на маленькій машині.

Завдання 13: MariaDB: переконайтесь, що ми не відкриті в мережі

cr0x@server:~$ sudo ss -lntp | grep -E '3306|mysqld|mariadbd'
LISTEN 0      80           127.0.0.1:3306       0.0.0.0:*    users:(("mariadbd",pid=2481,fd=21))

Що це означає: Прив’язано тільки до localhost, що добре для однозахідних застосунків.

Рішення: Якщо слухає на 0.0.0.0, виправте bind-address і правила фаєрвола негайно. Не «плануйте на потім». Потім — час, коли починається підбір облікових даних.

Завдання 14: SQLite: робимо бекап правильно (онлайн-безпечний)

cr0x@server:~$ sqlite3 /var/lib/myapp/app.db ".backup '/var/backups/app.db.bak'"

Що це означає: SQLite створює консистентну резервну копію навіть під час використання БД (в межах гарантій SQLite).

Рішення: Використовуйте .backup або backup API, а не наївний cp, якщо тільки ви не зупинили записувачів і не врахували WAL-файли.

Завдання 15: MariaDB: санітарна перевірка логічного бекапу

cr0x@server:~$ sudo mysqldump --single-transaction --quick --routines mydb | head
-- MySQL dump 10.19  Distrib 10.11.6-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: mydb
-- ------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;

Що це означає: Ви можете створити консистентний логічний дамп без агресивного блокування таблиць (для InnoDB).

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

Завдання 16: Місце на диску: чи ви про провал через дрібницю?

cr0x@server:~$ df -h /var/lib
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G   24G  0.9G  97% /

Що це означає: Ви в одному лог-спайку від простого простою. Бази даних ненавидять «диск заповнений». Вони покарають вас креативно.

Рішення: Очистіть логи, перемістіть бекапи за межі хоста, збільшіть диск. Це не опціональне обслуговування; це доступність.

Швидкий план діагностики

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

Перший крок: обмеження на хості (60 секунд)

  1. Свап і RAM: free -h. Якщо свап активний і RAM низька — це ваш головний підозрюваний.
  2. I/O wait: vmstat 1 5 або iostat -xz 1 5 (якщо встановлено). Високий wa означає, що сховище — частина проблеми.
  3. Місце на диску: df -h. Диски, що майже заповнені, викликають паузи, дивності і інколи корупцію після крашів.

Другий крок: реальність процесів (2–3 хвилини)

  1. Хто використовує CPU і пам’ять: ps відсортований за RSS і top. Якщо MariaDB домінує в RAM на хості 1GB — налаштуйте або перегляньте архітектуру.
  2. Кількість підключень (MariaDB): перевірте Threads_connected проти конфігурації пулу.
  3. Відкриті дескриптори файлів (обидва): якщо застосунок досягає лімітів, SQLite може не відкрити БД; MariaDB може не приймати підключення.

Третій крок: специфічні вузькі місця бази даних (5–15 хвилин)

  1. MariaDB: перевірте processlist на довгі запити; тимчасово увімкніть slow query log; шукайте тимчасові таблиці на диску і затримки InnoDB log.
  2. SQLite: перевірте режим WAL; busy_timeout і логи застосунку на предмет «database is locked»; знайдіть довгі транзакції; запустіть integrity_check, якщо підозрюєте корупцію.
  3. Рівень застосунку: переконайтесь, що ORM не робить N+1 запитів і що ви не відкриваєте транзакцію на кожен запит без потреби.

Якщо ви зробите лише одну річ: виправте свап і I/O wait перед тим, як торкатися налаштувань SQL. Більшість «проблем продуктивності бази даних» на маленьких VPS — це фактично «голод хоста», одягнений у SQL-костюм.

Три корпоративні міні-історії (з архіву рубців)

1) Інцидент через неправильне припущення: «SQLite не може конкурувати»

Невеликий внутрішній інструмент жив на одній VM. Він обробляв помірний потік подій, записував кілька рядків за секунду і показував панелі десь для десятка людей. Він стартував на SQLite, працював добре й ніхто не думав про нього місяцями — доки інженер не прочитав тред, де писали «SQLite не для продакшену».

Вони поспішно поміняли його на MariaDB, бо «продакшен = клієнт-сервер», так? Налаштували дефолтний MariaDB, перенесли схему і вказали застосунок на неї. У staging працювало. На ноуті працювало. Працювало день.

Потім VM почала зависати. Панелі таймаутили. Черга задач скупчувалась. Команда ганялася за «повільними запитами», додала кілька індексів і отримала короткочасне поліпшення. Але реальна причина була проста: інстанс MariaDB і застосунок конкурували за 2GB RAM, і під навантаженням хост свапив. Стрибки латенсі призводили до повторів. Повтори створювали більше підключень. Більше підключень — більше пам’яті. Відбулося спіралеобразне зростання.

Неправильне припущення було не про SQL-функції. Воно було про поведінку систем. SQLite працював, бо мав менший слід і не викликав свапу. MariaDB теж могла б підійти, але не з дефолтним пулом підключень і без бюджетування пам’яті.

Виправлення не було героїчним: вони повернулись до SQLite, увімкнули WAL, додали busy timeout і переробили застосунок для пакетної записи. Інцидент закінчився не масштабним проривом, а нагадуванням, що «production-ready» означає «операційно відповідний», а не «найпопулярніший у вакансіях».

2) Оптимізація, що відвернулась: «Зробити SQLite швидшим»

API на бюджетному VPS використовував SQLite. Записи були невеликі, але часті. Хтось помітив іноді латенсію запису і вирішив «оптимізувати» через зміни pragma: synchronous=OFF, більший кеш, агресивне тимчасове зберігання в пам’яті. Бенчмарки на машині розробника виглядали фантастично. Вони задеплоїли в п’ятницю, бо оптимізм — це джерело енергії.

Кілька днів воно дійсно стало швидше. Потім VPS несподівано перезавантажився — технічне обслуговування гіпервізора, без попередження. Застосунок піднявся, почав відмовляти запити. Файл SQLite не був катастрофічно пошкоджений, але мав несумісність на рівні прикладної логіки. Кілька критичних рядків зникли з таблиці, яка мала бути додатковою.

Дебаг був болісним, бо база була «структурно в порядку». Просто відсутні записи, які не встигли дійти до диска. Це ціна synchronous=OFF: ви попросили SQLite обманути вас щодо довговічності, і воно це зробило.

Вони відновилися з бекапів і переробили частину подій. Потім повернули pragma до розумних значень, залишили WAL і оптимізували поведінку застосунку: менше комітів, більше пакетування, коротші транзакції та коректні індекси. Продуктивність залишилася хорошою. Дані перестали зникати.

3) Нудна, але правильна практика, що врятувала день: «Тест відновлення»

Невеликий SaaS працював на одному VPS з MariaDB. Нічого складного: один primary, без реплік, без кластерів, без хитрих інструментів. Але оператор мав ритуал: щотижня тестове відновлення на одноразовому VM. Не теоретичний план, а реальне відновлення.

Одного ранку диск VPS почав повертати помилки I/O. Служба бази стала падати. Файлова система перемонтувалася в режим лише для читання. Заявки в службу підтримки прийшли, потім припинилися, бо застосунок був недоступний. Оператор не намагався робити героїчних відновлень. Він зупинив сервіс, зібрав логи і підняв новий VPS.

Оскільки відновлення тестували регулярно, кроки відновлення були задокументовані й правильні. Вони відновили нічний дамп, проиграли невелику чергу подій і підняли сервіс. Простій був дратівливим, але обмеженим. Ніякої імпровізації під стресом.

Нудна практика була не «використовувати MariaDB». Вона була «тестувати відновлення». Бази даних ламаються поза вашим графіком, тож ваше відновлення не може бути нетестованою гіпотезою.

Поширені помилки: симптом → корінь → виправлення

1) Помилки «Database is locked» у SQLite при легкому трафіку

Симптоми: спорадичні 500, в логах database is locked, спайки під час фонових задач.

Корінь: довгі транзакції (часто «BEGIN; робота; виклик зовнішнього сервісу; COMMIT»), відсутній busy timeout, режим rollback journal або занадто багато записувачів.

Виправлення: увімкніть WAL; скоротіть транзакції; встановіть busy timeout; пакетизуйте записи; забезпечте лише один шлях запису, якщо потрібно.

2) MariaDB «Too many connections» на маленькому VPS

Симптоми: помилки застосунку при підключенні; логи БД показують обмеження підключень; CPU росте під час інцидентів.

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

Виправлення: зменшіть розмір пулу; впровадьте правильний пуулінг; обмежте повтори з бекофом; моніторте Threads_connected.

3) Періодичні паузи 2–10 секунд на записах MariaDB

Симптоми: стрибки латенсі під час комітів; CPU не завантажено; користувачі скаржаться на «випадкову повільність».

Корінь: затримки fsync на сховищі, дешевий диск VPS, поведінка InnoDB при скиданні журналів.

Виправлення: перейдіть на кращий клас сховища; зменшіть write amplification (пакетуйте записи); переконайтесь, що конфіг InnoDB адекватний; уникайте насичення диска іншими завданнями одночасно.

4) SQLite «працює в dev», але падає в контейнері

Симптоми: не вдається відкрити файл БД, permission denied або файл БД скидається несподівано.

Корінь: неправильний монтування тома, неправильний UID/GID або ефемерна файлова система в контейнері.

Виправлення: явний персистентний том; правильна власність файлу; налаштуйте шлях файлу через конфіг; переконайтесь, що атомарні деплоя не замінюють файл БД.

5) MariaDB повільно займає всю RAM з часом

Симптоми: використання пам’яті росте; починається свап; продуктивність деградує через дні.

Корінь: занадто великий буферний пул, перехідні буфери на підключення, надто багато конкурентних підключень.

Виправлення: зменшіть буферний пул; зменшіть конкурентність; перепишіть дорогі запити; моніторьте тимчасові таблиці і сортування.

6) «Бекап є», але відновлення не працює

Симптоми: ви запускаєте drill відновлення і воно помилкове, або відновлені дані неконсистентні.

Корінь: SQLite-бекап зроблено неправильно під час активного WAL; MariaDB-дамп зроблено без транзакційної консистенції; відсутні процедури; неправильні charset/collation припущення.

Виправлення: SQLite: використовуйте .backup або зупиніть записувачів і копіюйте DB+WAL; MariaDB: використовуйте --single-transaction для InnoDB і регулярно тестуйте відновлення.

7) «SQLite повільний» під час великих звітів

Симптоми: довгі SELECT-блоки пишучі операції; веб-запити таймаутяться.

Корінь: довгі транзакції читання, що тримають снапшоти; недостатні індекси; великі повні скани на дешевих дисках.

Виправлення: додайте індекси; пагінація; виносьте звітування на репліку/ETL-файл; розгляньте MariaDB, якщо звітування і записи мають сусідити з більшою конкурентністю.

8) Оновлення MariaDB ламає застосунок несподівано

Симптоми: запити поводяться інакше; змінюється суворість; невідповідність плагіна аутентифікації.

Корінь: версійно-залежні дефолти і відмінності сумісності між MySQL/MariaDB; недостатнє тестування у staging.

Виправлення: закріплюйте версії; тестуйте оновлення в staging з реальними даними; фіксуйте диф конфігів; майте план відкату.

Чек-листи / поетапний план

Покроково: обираємо SQLite на маленькому VPS (шлях «відпустити в продакшен»)

  1. Підтвердіть форму навантаження: один вузол, низько-до-помірні записи, немає потреби в мульти-хостових записах.
  2. Увімкніть режим WAL: встановіть PRAGMA journal_mode=WAL; при ініціалізації БД.
  3. Встановіть розумну довговічність: віддавайте перевагу synchronous=FULL для коректності; розглядайте NORMAL тільки якщо ви готові до втрати даних при ривку живлення.
  4. Встановіть busy timeout: щоб уникнути миттєвих збоїв при короткій конкуренції; все одно зменшіть довжину транзакцій.
  5. Проєктуйте для одного записувача: серіалізуйте write-важкі шляхи або робіть пакетні записи через чергу в процесі.
  6. Резервні копії: використовуйте .backup; відправляйте бекапи за межі хоста; тестуйте відновлення щомісяця.
  7. Спостережуваність: логування латенсів запитів у застосунку; ловіть помилки «database is locked» з контекстом.

Покроково: якщо ви наполягаєте на MariaDB на маленькому VPS (робіть це безпечно)

  1. Прив’язка до localhost: виставляйте БД в мережу лише якщо дійсно потрібен віддалений доступ.
  2. Бюджетуйте RAM: розмір буферного пулу консервативний; залишайте пам’ять для OS і застосунку.
  3. Налаштуйте пуулінг підключень: невеликі розміри пулів; обмежуйте конкурентність; уникайте підключення/відключення на кожен запит.
  4. Увімкніть мінімальну спостережуваність: slow query log з розумним порогом при діагностиці; стежте за тимчасовими таблицями на диску.
  5. Дріли бекапів і відновлення: автоматизуйте дампи; оберніть; тестуйте відновлення на чисту інстанцію.
  6. План оновлень: закріплюйте версії; тестуйте; плануйте; майте відкат.

Покроково: шлях міграції (SQLite зараз, MariaDB пізніше) без драм

  1. Тримайте SQL портативним: уникайте SQLite-специфічних лайфхаків якщо плануєте міграцію (наприклад, покладатися на гнучке типізування як на «фічу»).
  2. Використовуйте явні міграції: версіонуйте схему, не покладайтесь на «створення таблиць при старті».
  3. Абстрагуйте доступ до БД: тримайте невеликий шар доступу до даних; не розкидайте сирий SQL по коду, якщо не любите археологію.
  4. План експорту/імпорту: використовуйте повторюваний формат дампу (CSV для таблиць або експорт на рівні застосунку) і перевіряйте кількість рядків та контрольні суми.
  5. Короткий паралельний запуск: пишіть в один, читайте з іншого лише під час планового вікна cutover, якщо застосунок це підтримує.
  6. Cutover з freezed-write: зупиніть записи, зробіть фінальний синх, переключіться, провалідуйте і відновіть роботу.

Часті запитання

1) Чи «SQLite production-ready» для веб-застосунку на VPS?

Так, якщо ваше навантаження підходить: один вузол, помірна конкурентність записів, короткі транзакції, режим WAL і реальні бекапи. «Production-ready» — про поводження під відмовою, а не про наявність демона.

2) Скільки одночасних користувачів може витримати SQLite?

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

3) Чи вирішує WAL проблему блокувань SQLite?

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

4) Можу я помістити SQLite на NFS, щоб ділитися між серверами?

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

5) Чому MariaDB здається повільнішою на маленькому VPS, ніж SQLite?

Не завжди повільніша, але може бути: більше накладних витрат на пам’ять, більше потоків, більше fsync-натиску і мережевий протокол навіть на localhost. На обмежених хостах ці накладні витрати стають помітними латенсіями.

6) Який найпростіший безпечний бекап для SQLite?

sqlite3 app.db ".backup 'app.db.bak'", потім копіюйте бекап за межі хоста. Тестуйте відновлення. Якщо просто копіювати живий файл БД без урахування WAL, можна отримати неконсистентний бекап.

7) Який найпростіший безпечний бекап для MariaDB на одному VPS?

mysqldump --single-transaction для таблиць InnoDB, заплановано в нефіговий час, ротація, копіювання за межі хоста і тест відновлення. Фізичні бекапи швидші, але додають операційну складність.

8) Коли мені переходити з SQLite на MariaDB?

Коли потрібен мульти-хостовий доступ, висока конкурентність записів, багатший операційний інструментарій або ви витрачаєте час на обхід обмеження «один писач» в SQLite. Також коли потрібно надійно масштабувати поза одним вузлом.

9) Чи MariaDB безпечніша за SQLite?

Не автоматично. Обидві можуть бути безпечними або небезпечними залежно від конфігурації та операційної дисципліни. Ризики SQLite зазвичай навколо блокувань і коректності бекапів; ризики MariaDB часто — навколо налаштування ресурсів, експозиції і операційної складності.

10) Чи можу я використовувати обидві?

Так, і іноді це найчистіший підхід: SQLite для локального стану і черг; MariaDB — для розподілених транзакційних даних. Просто будьте чесні щодо складності, яку ви додаєте.

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

  • Якщо вагаєтесь: виміряйте вашу конкурентність записів і тривалість транзакцій. Це ключова змінна. Не «функції».
  • Якщо ви на MariaDB на маленькому VPS: перевірте свап, розмір buffer pool і кількість підключень. Обмежте пули. Прив’яжіть до localhost. Увімкніть slow query лог тимчасово при діагностиці.
  • Якщо ви на SQLite: увімкніть WAL, встановіть busy timeout і проведіть аудит коду на предмет довгих транзакцій. Реалізуйте рутинний .backup і прогоніть тест відновлення.
  • Якщо ви плануєте рости: тримайте міграції схеми дисциплінованими і SQL портативним, щоб перехід на MariaDB пізніше був планованою зміною, а не нічною переробкою.

«Правильна» база даних для маленького VPS-проєкту — та, що не заважає вам і водночас чесна щодо відмов. SQLite часто є такою базою. MariaDB відмінна, коли вам дійсно потрібні її можливості. Не платіть операційний податок зарані, якщо не впевнені, що використовуватимете ті сервіси, за які заплатили.

← Попередня
MariaDB vs PostgreSQL Container Storage: Bind Mount vs Volume Performance Truth
Наступна →
USB-перенаправлення в Proxmox: відключення, автоматичне призупинення живлення та виправлення стабільності

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