Центр помощи

Пошаговые руководства по серверам, сетям и аккаунту.

Linux-сервер

Настройка MySQL и MariaDB на VPS с 1 или 2 ГБ ОЗУ: какие параметры my.cnf действительно важны

Настройка MySQL и MariaDB на VPS с 1 или 2 ГБ ОЗУ: какие параметры my.cnf действительно важны

На VPS с 1 или 2 ГБ ОЗУ MySQL и MariaDB работают быстро и стабильно, если несколько параметров my.cnf соответствуют реальному объему памяти: innodb_buffer_pool_size, max_connections, лимиты временных таблиц и performance_schema. В этой инструкции — готовые конфигурации для обоих объемов, безопасное применение и проверка в Debian, Ubuntu, Rocky Linux и AlmaLinux, а также поиск медленных запросов, которые никакая настройка не исправит.

Примеры рассчитаны на облачные тарифы JUSTG 01 (1 ГБ ОЗУ, 20 ГБ SSD) и 02 (2 ГБ ОЗУ, 40 ГБ SSD), где база данных работает на одном VPS с веб-сервером и PHP. Выполняйте команды от root; если mysql запрашивает пароль, добавьте -u root -p. В MariaDB 11 (Debian 13), если команды mysql… нет, используйте вариант с именем mariadb… (mariadb, mariadb-dump, mariadb-dumpslow).

Шаг 1: Узнайте версию и текущее потребление памяти

Некоторые параметры есть только в MySQL или только в MariaDB, поэтому сначала выясните, какой сервер и какая версия у вас установлены и сколько памяти он занимает сейчас.

mysql --version                 # MariaDB 11 (Debian 13): mariadb --version
mysql -e "SELECT VERSION();"
free -h
ps -o pid,rss,cmd -C mysqld,mariadbd    # RSS = memory really used, in KB

RSS указан в килобайтах (350000 — это примерно 340 МБ). Если при обычной нагрузке available в выводе free -h уже меньше 150 МБ, сначала уменьшайте параметры, а не увеличивайте. Типичные версии: в Debian 12 — MariaDB 10.11, в Debian 13 — MariaDB 11.8, в Ubuntu 22.04/24.04 доступны MySQL 8.0 или MariaDB.

Шаг 2: Сначала сделайте резервную копию баз и конфигурации

Изменение конфигурации не трогает данные, а вот ошибка во время тестов — может. Сначала сделайте полный дамп и скопируйте каталог конфигурации. Если диск почти заполнен, освободите место по инструкции Диск заполнен в Linux: как найти и освободить место на VPS.

df -h /                         # enough free space for the dump?
mkdir -p /root/db-backup
mysqldump --all-databases --single-transaction --routines --events \
  | gzip > /root/db-backup/all-$(date +%F).sql.gz
# MariaDB 11 (Debian 13): use mariadb-dump with the same options

cp -a /etc/mysql /root/db-backup/etc-mysql-$(date +%F)
# Rocky / AlmaLinux: cp -a /etc/my.cnf /etc/my.cnf.d /root/db-backup/
ls -lh /root/db-backup

--single-transaction делает согласованную копию таблиц InnoDB без блокировки сайта (на MyISAM это не распространяется, поэтому такие таблицы выгружайте в тихое время).

Шаг 3: Найдите, где хранится конфигурация

Не редактируйте большой файл по умолчанию. Создайте собственный небольшой файл в каталоге include: он читается после стандартных, поэтому его значения имеют приоритет, а обновления пакетов его не перезапишут.

СистемаКаталог includeОсновной файл сервера
Debian 12/13, Ubuntu 22.04/24.04 с MariaDB/etc/mysql/mariadb.conf.d/50-server.cnf
Ubuntu 22.04/24.04 с MySQL 8.0/etc/mysql/mysql.conf.d/mysqld.cnf
Rocky Linux / AlmaLinux (MariaDB или MySQL)/etc/my.cnf.d/mariadb-server.cnf или mysql-server.cnf

В любом случае создайте в этом каталоге файл zz-tuning.cnf. Файлы читаются в алфавитном порядке, поэтому префикс zz- гарантирует, что ваш загрузится последним; имя обязательно должно заканчиваться на .cnf.

Шаг 4: Рекомендуемые параметры для 1 ГБ и 2 ГБ

Начните с профиля под ваш объем ОЗУ. Оба оставляют место для веб-сервера и PHP на том же VPS.

VPS с 1 ГБ

# zz-tuning.cnf - 1 GB VPS, database shares the server with nginx/Apache and PHP
[mysqld]
performance_schema      = OFF
skip-name-resolve

innodb_buffer_pool_size = 256M
max_connections         = 40
thread_cache_size       = 8

tmp_table_size          = 32M
max_heap_table_size     = 32M

table_open_cache        = 400
table_definition_cache  = 400
key_buffer_size         = 16M

# 1 = safest (default), see Step 5
innodb_flush_log_at_trx_commit = 1

VPS с 2 ГБ

# zz-tuning.cnf - 2 GB VPS, database shares the server with nginx/Apache and PHP
[mysqld]
performance_schema      = OFF
skip-name-resolve

innodb_buffer_pool_size = 512M
max_connections         = 80
thread_cache_size       = 16

tmp_table_size          = 64M
max_heap_table_size     = 64M

table_open_cache        = 1000
table_definition_cache  = 800
key_buffer_size         = 32M

# 1 = safest (default), see Step 5
innodb_flush_log_at_trx_commit = 1
  • innodb_buffer_pool_size — кеш InnoDB для данных и индексов, самый важный параметр. Известное правило «70–80% ОЗУ» относится к серверам только под базу. Если на том же VPS работает веб-стек, начните примерно с 25% ОЗУ; VPS только под базу может выделить около 512M при 1 ГБ и 1G при 2 ГБ.
  • max_connections — каждое подключение может выделить собственные буферы (шаг 10), поэтому значение по умолчанию 151 на маленьком тарифе рискованно. PHP-сайту редко нужно больше, чем число воркеров PHP-FPM плюс несколько подключений для cron и инструментов администрирования.
  • tmp_table_size и max_heap_table_size — лимит временных таблиц в памяти (GROUP BY, ORDER BY, DISTINCT). Действует меньшее значение, поэтому задавайте их одинаковыми. Большие значения в основном лишь повышают расход памяти на запрос в худшем случае.
  • table_open_cache и table_definition_cache — нескольких сотен хватает для VPS с парой сайтов; увеличивайте, если Opened_tables продолжает расти (шаг 6).
  • performance_schema = OFF — в MySQL 8 она включена по умолчанию и может занимать больше 100 МБ, в MariaDB уже выключена. Вы теряете часть диагностических представлений — разумная цена для маленького тарифа.
  • skip-name-resolve — никаких DNS-запросов при новых подключениях, поэтому вход не зависает, когда DNS тормозит. Учетные записи после этого должны задаваться по IP-адресу или localhost, а не по имени хоста.
  • key_buffer_size — кеширует только индексы MyISAM (в MariaDB по умолчанию 128 МБ); сайтам на InnoDB нужно совсем немного.

Перед включением skip-name-resolve выведите список учетных записей; если в host указано имя, например app.example.com, пересоздайте эту запись с IP-адресом (например, 203.0.113.10). В MySQL 8 заодно проверьте, сколько места занимают бинарные логи:

mysql -e "SELECT user, host FROM mysql.user;"
mysql -e "SHOW BINARY LOGS;"      # MySQL 8: how much space binary logs use

Строки для вашей версии сервера

Некоторые параметры есть только в одном из серверов. Добавьте подходящий блок в тот же файл в раздел [mysqld] и для 1 ГБ скорректируйте значения, как указано в комментариях:

# --- MySQL 8.0.30 or newer only (Ubuntu MySQL 8.0, MySQL 8.4) ---
# Redo log: default 100M is fine on 1 GB; 256M for write-heavy sites on 2 GB
innodb_redo_log_capacity   = 256M
# RAM cap for internal temporary tables: 64M on 1 GB, 128M on 2 GB
temptable_max_ram          = 128M
# Keep binary logs 3 days instead of the default 30
binlog_expire_logs_seconds = 259200

# --- MariaDB, or MySQL older than 8.0.30 ---
# Redo log: default 96M (MariaDB) is fine on 1 GB; 128M-256M on 2 GB
innodb_log_file_size       = 128M

Бинарные логи нужны для репликации и восстановления на момент времени; за 30 дней логи загруженного сайта могут заполнить диск на 20 ГБ. Если вам не нужно ни то, ни другое, MySQL 8 также принимает skip-log-bin.

Никогда не копируйте параметр, который ваш сервер не знает. MariaDB при запуске остановится с ошибкой unknown variable 'innodb_redo_log_capacity', а MySQL 8 отвергнет query_cache_size, потому что кеш запросов из него удален. Если после изменения сервер не запускается, посмотрите журнал (шаг 6), исправьте строку и запустите снова.

Шаг 5: innodb_flush_log_at_trx_commit — честно о компромиссе

Этот параметр определяет, сколько подтвержденных данных можно потерять, если у VPS пропадет питание или упадет ядро. Выбирайте его по тому, что ваш сайт может позволить себе потерять, а не по бенчмаркам.

  • 1 (по умолчанию) — журнал redo сбрасывается на диск при каждом коммите, подтвержденные данные не теряются. Для магазинов, биллинга, учетных записей и всего, что связано с деньгами.
  • 2 — при коммите данные передаются ОС, а на диск сбрасываются примерно раз в секунду. Сбой ОС или VPS может стоить до ~1 секунды транзакций; падение только базы — ничего. Подходит для блогов, кешей и статистики.
  • 0 — запись и сброс примерно раз в секунду, поэтому даже падение самой базы может стоить ~1 секунды. Редко лучше, чем 2; не рекомендуется.

Значение 2 может заметно ускорить множество мелких записей, но это обмен надежности на скорость. Если сомневаетесь, оставьте 1; объединение мелких записей в одну транзакцию в приложении — более безопасный выигрыш.

Шаг 6: Безопасно примените настройки и проверьте результат

Проверьте файлы, перезапустите службу и сразу посмотрите журнал. Используйте имя службы вашего сервера.

# 1. Show the options the server will read (MariaDB: mariadbd --print-defaults)
mysqld --print-defaults
# MySQL 8.0.16+ only: check the option files without starting the server
mysqld --validate-config

# 2. Restart and read the log
# Service name: mariadb (MariaDB) | mysql (MySQL on Ubuntu) | mysqld (MySQL on Rocky/Alma)
systemctl restart mariadb
systemctl status mariadb --no-pager
journalctl -u mariadb -n 30 --no-pager

Затем убедитесь, что новые значения действуют, и посмотрите счетчики:

mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','max_connections','tmp_table_size','performance_schema','skip_name_resolve');"

mysql -e "SHOW GLOBAL STATUS WHERE Variable_name IN ('Uptime','Max_used_connections','Created_tmp_tables','Created_tmp_disk_tables','Opened_tables','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests','Aborted_connects');"

Счетчики статуса накапливаются с момента последнего запуска, поэтому смотрите их не раньше чем через сутки обычной нагрузки (Uptime указан в секундах).

  • Max_used_connections близко к max_connections: приложение открывает слишком много подключений или запущено слишком много воркеров PHP. Исправьте это, прежде чем поднимать лимит.
  • Created_tmp_disk_tables больше ~25% от Created_tmp_tables: найдите эти запросы в журнале медленных запросов (шаг 7). Индекс обычно помогает больше, чем увеличение tmp_table_size.
  • Innodb_buffer_pool_reads больше 1% от Innodb_buffer_pool_read_requests: рабочий набор данных не помещается в пул. Увеличивайте его, только если free -h показывает свободную доступную память.
  • Opened_tables после прогрева продолжает быстро расти: увеличивайте table_open_cache шагами примерно по 200.

Растущий Aborted_connects часто означает, что боты добрались до порта 3306; если он не должен быть открыт наружу, закройте его, как описано в статье Настройка файрвола в Linux: UFW и firewalld на VPS.

Шаг 7: Найдите медленные запросы через slow query log

Большинство жалоб «база тормозит» — это несколько плохих запросов, а не конфигурация. Журнал медленных запросов записывает каждый запрос, выполняющийся дольше long_query_time секунд.

# Turn the slow query log on now (until the next restart)
mysql -e "SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 1;"
mysql -e "SHOW VARIABLES LIKE 'slow_query_log_file';"

Чтобы журнал оставался включенным после перезапуска, добавьте в zz-tuning.cnf строки:

# Add to zz-tuning.cnf to keep it after restarts
[mysqld]
slow_query_log      = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time     = 1
# Rocky / AlmaLinux with MariaDB: /var/log/mariadb/mysql-slow.log

Новое значение long_query_time действует только для новых подключений. Дайте журналу собрать данные за сутки обычной нагрузки, затем получите сводку:

# Top 10 query patterns by total time (MariaDB 11: mariadb-dumpslow)
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

# Turn the log off again when you are done
mysql -e "SET GLOBAL slow_query_log = 0;"

mysqldumpslow группирует похожие запросы, заменяя числа на N, а строки на 'S', и показывает, сколько раз выполнялся каждый и сколько времени он занял. Начинайте сверху: запрос, выполненный 10 000 раз по 0,2 секунды, обходится гораздо дороже разового на 5 секунд. Пока журнал включен, он растет, поэтому потом выключите его или убедитесь, что его ротирует конфигурация в /etc/logrotate.d/.

Шаг 8: Основы индексов и EXPLAIN

Добавьте EXPLAIN перед медленным SELECT, чтобы увидеть, как сервер его выполняет. В примере используется условная таблица orders:

EXPLAIN SELECT id, total FROM orders
WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;

SHOW INDEX FROM orders;

ALTER TABLE orders ADD INDEX idx_customer_created (customer_id, created_at);

-- Run EXPLAIN again: type should change from ALL to ref, key = idx_customer_created
EXPLAIN SELECT id, total FROM orders
WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;

В выводе type = ALL вместе с key = NULL означает полный просмотр таблицы, а ref, eq_ref или range — что используется индекс. Большая оценка rows для запроса, который возвращает 20 строк, указывает на отсутствующий индекс, а Using filesort или Using temporary в Extra — на дополнительную сортировку.

  • Сначала индексируйте столбцы из WHERE (сравниваемые через = — в начале), затем столбец из ORDER BY.
  • Не применяйте функции к индексированным столбцам: WHERE DATE(created_at) = '2026-11-01' не может использовать индекс, а диапазон created_at >= '2026-11-01' AND created_at < '2026-11-02' — может.
  • Не индексируйте все подряд: каждый индекс замедляет запись и занимает память буферного пула.
Добавление индекса в большую таблицу требует времени, ресурсов CPU и дополнительного места на диске. MySQL 8 и MariaDB строят большинство индексов онлайн, но нагрузка на сервер всё равно возрастает, поэтому делайте это в тихое время и после резервной копии из шага 2.

Шаг 9: mysqltuner как второе мнение

mysqltuner анализирует счетчики сервера и предлагает возможные улучшения. Он есть в репозиториях Debian и Ubuntu; в Rocky/AlmaLinux обычно доступен из EPEL.

apt install mysqltuner          # Debian / Ubuntu
mysqltuner

Пользуйтесь им осторожно: запускайте не раньше чем через 24 часа обычной нагрузки и помните, что он ничего не знает о PHP и веб-сервере на том же VPS и может предложить буферный пул, который не оставит им места. Сверяйте каждый совет с free -h и меняйте по одному параметру за раз. Воспринимайте его вывод как список вопросов, а не как инструкцию.

Шаг 10: Не доводите до OOM killer

Когда заканчивается ОЗУ, OOM killer в Linux завершает процесс, занимающий больше всего памяти, — обычно это база данных. Прежде чем что-то увеличивать, грубо оцените худший случай:

# Per-connection buffers (about 2-3 MB per connection with defaults)
mysql -e "SHOW VARIABLES WHERE Variable_name IN ('sort_buffer_size','join_buffer_size','read_buffer_size','read_rnd_buffer_size','thread_stack');"

# Rough worst case, 1 GB profile:
#   buffer pool 256 MB + overhead ~60 MB + 40 connections x ~2.5 MB ~100 MB
#   = ~420 MB, leaving ~500 MB for the OS, web server and PHP

# Has the kernel already killed the database?
journalctl -k | grep -i -E 'out of memory|killed process'

Реальный расход обычно ниже, потому что эти буферы выделяются только тогда, когда они нужны запросу; при этом каждый запрос может еще использовать временные таблицы до tmp_table_size, так что рассчитывайте на пик. Никогда не поднимайте глобально sort_buffer_size или join_buffer_size до нескольких мегабайт: умноженные на max_connections, они быстро превысят тариф с 1 ГБ. Если большой буфер сортировки нужен одному отчету, выполните SET SESSION sort_buffer_size = 8 * 1024 * 1024; только в этом подключении.

Добавьте swap как страховку от кратковременных пиков — см. Как добавить swap на Linux VPS. Swap предотвращает падение, но не делает быстрой базу, которой не хватает ОЗУ; если после настройки памяти по-прежнему мало, настоящее решение — тариф побольше, см. Как увеличить RAM, SSD или добавить IP на VPS.

Частые вопросы

Стоит ли включить кеш запросов (query cache)?

Нет. Из MySQL 8 он удален, а в MariaDB (где он выключен по умолчанию) при параллельной записи становится узким местом из-за блокировок. Кешируйте на стороне приложения, например с помощью объектного кеша.

После перезапуска мои изменения не применились. Почему?

Обычно файл лежит в каталоге, который не подключается, его имя не заканчивается на .cnf, или файл, прочитанный позже, снова задает тот же параметр. mysqld --print-defaults (в MariaDB — mariadbd --print-defaults) показывает итоговый список параметров, а SHOW VARIABLES — то, что действует на самом деле.

Можно ли запустить базу на тарифе mini с 512 МБ?

Для небольшого сайта — да, со swap и экономными настройками: например, буферный пул 128M, 20 подключений и выключенная performance_schema. При таком объеме MariaDB обычно легче; более нагруженным сайтам удобнее тариф от 1 ГБ.

Если базу продолжает завершать OOM killer или вы не уверены, какое значение менять, создайте тикет по адресу https://www.justg.com/submitticket.php и приложите IP сервера, ваш zz-tuning.cnf и вывод free -h. В JUSTG работает AI-поддержка 24/7 по тикетам, а вопросы, требующие участия человека, подхватывают инженеры.

Помог ли вам данный ответ?
Центр помощи
Linux-сервер Вход по SSH-ключу в Linux: настройка ключей ed25519 и отключение входа по паролю Любой сервер с публичным IPv4 через несколько минут после запуска начинают атаковать боты, перебирающие SSH-пароли. В этой инструкции… Linux-сервер Как безопасно сменить порт SSH и пароль root на Linux VPS Перенос SSH с порта 22 не остановит целенаправленную атаку, но уберёт из логов большую часть автоматического сканирования, а смена пароля… Linux-сервер Настройка файрвола в Linux: UFW и firewalld на VPS (IPv4 и IPv6) Только что установленный Linux-сервер отвечает на любом порту, который открыла какая-либо служба, поэтому файрвол — одна из первых вещей,… Linux-сервер Как добавить swap на Linux VPS (тарифы с 512 МБ и 1 ГБ ОЗУ) На облачном сервере с 512 МБ или 1 ГБ ОЗУ база данных, пул PHP-FPM и обновление пакетов легко исчерпывают память одновременно. Тогда OOM… Linux-сервер Диск заполнен в Linux: как найти и освободить место на VPS Ошибка «No space left on device» ломает на сервере почти всё: базы данных перестают писать, сайты отдают ошибки, обновления пакетов не…
AlipayUnionPayVISAMastercardPayPalUSDTstripe