Если у вас небольшой VDS (1–2 vCPU и 1–2 ГБ ОЗУ), то правильный базовый тюнинг MySQL/MariaDB даёт ощутимый выигрыш без сложных трюков. В этой статье собрал минимально необходимые шаги: понимание ограничений памяти и диска, настройка InnoDB (включая innodb_buffer_pool_size), полезные параметры журнала медленных запросов (slow query log), основы индексации и несколько диагностических приёмов через Performance Schema для осмысленной query optimization. Сразу предупрежу: магии нет. Есть дисциплина измерений и аккуратные изменения по одному за раз.
Ограничения малого VDS и приоритеты
Малый VDS чаще всего упирается в три вещи: память, случайный I/O и «взрывающиеся» по памяти соединения при пиках. Поэтому приоритизируем:
- максимально возможный буфер InnoDB, чтобы рабочий набор помещался в RAM;
- ограничение «дорогих» на соединение буферов и числа соединений;
- видимость проблем: включаем slow query log и делаем короткие циклы анализа-фикса;
- минимум фич, которые мало дают, но много стоят (например, чрезмерные статистики Performance Schema либо слишком агрессивные per-connection буферы).
Ключевая мысль: сначала обеспечить стабильную память и предсказуемый I/O, затем точечно оптимизировать запросы и индексы. Если текущих ресурсов не хватает даже после тюнинга — планируйте апгрейд на более мощный VDS.
Если думаете о подборе тарифа по CPU/RAM под вашу нагрузку, посмотрите разбор критериев выбора: как выбирать план VDS по CPU, RAM и диску.
Планирование памяти: буферы InnoDB и стоимость соединений
Размеры для innodb_buffer_pool_size
Буферный пул InnoDB — главный потребитель памяти и главный ускоритель чтения/записи. Базовые ориентиры для малых инстансов:
- 512 МБ RAM:
innodb_buffer_pool_size256–320 МБ. - 1 ГБ RAM: 512–700 МБ.
- 2 ГБ RAM: 1–1.2 ГБ.
Держите запас под сам сервер, системный кеш страницы, binlog (если включён), таблицы Performance Schema, временные таблицы и соединения. Пул лучше чуть меньше идеала, чем риск оверкоммита и свопа.
Per-connection буферы: незаметные убийцы памяти
Каждое соединение может потреблять дополнительные буферы: sort_buffer_size, join_buffer_size, read_buffer_size, read_rnd_buffer_size, а также память под временные таблицы (tmp_table_size/max_heap_table_size). Умножьте их на одновременные соединения — легко получить сотни мегабайт сверх ожиданий.
Базовые рекомендации для малого VDS:
max_connections: задайте реальный потолок приложения (например, 50–100), а не «на вырост».sort_buffer_size,join_buffer_size: начните с 1–2 МБ и повышайте точечно, если видите регулярные крупные сортировки/соединения.tmp_table_sizeиmax_heap_table_size: держите равными и умеренно консервативными (например, 32–64 МБ), контролируя метрикуCreated_tmp_disk_tables.
Меньше — лучше, пока не доказано обратное реальными метриками. Иначе получите нехватку памяти и своп.
Если проект ещё на виртуальный хостинг, а вам нужен полный контроль над конфигурацией MySQL — переезд на VDS даст свободу тюнинга и предсказуемые ресурсы. Для безпростойного переноса см. практику: миграция сайта без простоя или переезд с виртуального хостинга на VDS.

InnoDB: настройки по умолчанию и точечные улучшения
Буферный пул и его инстансы
Помимо innodb_buffer_pool_size, учитывайте innodb_buffer_pool_instances. На малых объёмах RAM достаточно 1 (или 2 при пуле > 1 ГБ). Большое число инстансов на небольшом пуле приводит к избыточной фрагментации и накладным расходам.
Redo-журналы: разница MySQL и MariaDB
Если используете MySQL 8.0+, обратите внимание, что вместо старого innodb_log_file_size применяется innodb_redo_log_capacity (суммарная ёмкость редо-журналов). Для MariaDB по-прежнему актуальны innodb_log_file_size и innodb_log_files_in_group. Слишком маленький редо приведёт к частым flush и I/O-штормам, слишком большой — к долгой аварийной восстановительной фазе.
- Стартовые значения: MySQL 8.0 —
innodb_redo_log_capacity256–1024 МБ в зависимости от нагрузки записи; MariaDB — 2×256 МБ или 2×512 МБ (innodb_log_file_size×innodb_log_files_in_group). - Проверяйте отношение Log Writes к fsync и латентность диска; цель — избежать постоянных принудительных сбросов.
Синхронизация и надёжность: innodb_flush_log_at_trx_commit и сопутствующие
innodb_flush_log_at_trx_commit=1 даёт максимальную надёжность — безопасный дефолт для критичных данных и на серверах с надёжным хранилищем. Значение 2 часто ускоряет вставки/обновления при умеренной потере устойчивости к внезапному отключению питания. 0 обычно не рекомендую на проде. Если используется бинарный лог, учитывайте sync_binlog=1 для сильной консистентности или 0/100 для компромисса.
Метод флеша и смежные опции
innodb_flush_method часто оставляют по умолчанию, но на многих платформах O_DIRECT снижает двойное кэширование (особенно если буферный пул большой). Проверьте поведение в нагрузочных тестах. innodb_doublewrite оставьте включённым для целостности, отключать — только при наличии чётких гарантий на хранилище и осознанного риска. innodb_adaptive_hash_index обычно помогает OLTP-нагрузкам; отключение имеет смысл лишь при наблюдаемом latch-contention.
file-per-table и формат хранения
innodb_file_per_table включён по умолчанию и упрощает управление пространством. Изредка помогает дефрагментация путём OPTIMIZE TABLE отдельных крупных таблиц, но делайте это в тихие часы и после полного бэкапа.
Query Cache
В MySQL 8.0 он удалён. В MariaDB Query Cache всё ещё существует, однако на многопоточном доступе часто вредит из-за глобальных блокировок. На малом VDS со смешанной нагрузкой лучше оставить выключенным.
Slow query log: быстрый поиск медленных мест
Журнал медленных запросов (slow query log) — обязательный инструмент. Он почти бесплатен при разумных порогах и радикально сокращает время поиска «узких горлышек».
Базовые параметры:
slow_query_log=ON — включить лог;long_query_time=0.5–1.0 сек для начала;log_queries_not_using_indexes=ON — полезно на этапе аудита, но постоянно не держите;min_examined_row_limit≥1000 — отсечёт «мусор»;log_slow_admin_statements=ON — фиксирует тяжёлые операции обслуживания.
Разбор лога начинайте с агрегирования по «сигнатурам» запросов, а не по конкретным значениям параметров. Цель — найти шаблоны, которые стабильно «жгут» CPU/диск. Дальше — query optimization через индексы, переписывание WHERE/JOIN, уменьшение объёма возвращаемых данных, покрывающие индексы и т.д.
После активного этапа анализа снизьте детализацию slow log, чтобы уменьшить накладные расходы и размер файлов логов.
Performance Schema и sys: диагностика без гадания
Performance Schema по умолчанию включён и даёт массу сигналов: где ожидания (waits), какие объекты «горячие», какие запросы чаще всего самые дорогие по времени/строкам. На малом VDS важно не раздувать потребление памяти — оставьте включённым, но не активируйте все потребители событий без необходимости.
На что смотреть в первую очередь:
- Общие «узкие места»: ожидания по I/O (таблицы/индексы), mutex/lock contention;
- Digest топ-стейтментов: шаблоны запросов с наибольшей стоимостью и их распределение;
- «Горячие» таблицы и индексы: дисбаланс использования, излишние полные сканы.
В паре со slow query log это позволяет быстро сузить поиск и переходить к точным поправкам в индексации или переписыванию запросов.

Индексы: меньше таблиц-сканов, больше попаданий в память
Правильные индексы — это 80% успеха. InnoDB — B-дерево, поэтому порядок полей в составном индексе критичен.
- Составные индексы по селективности слева направо: поле с высокой селективностью — ближе к началу. Пример:
(status, created_at)дляWHERE status=? AND created_at BETWEEN .... - Покрывающие индексы: если запрос читает только поля индекса, сервер избегает чтения строк (Extra: Using index).
- Префиксные индексы на длинных строках:
email(64)вместо полного, если селективность приемлема. - Функциональные индексы: MySQL 8.0 — выражения и/или генерируемые столбцы; MariaDB — виртуальные/материализованные колонки.
- FULLTEXT для текстового поиска вместо LIKE с ведущим процентом.
- Первичный ключ — компактный и монотонный (INT/BIGINT AUTO_INCREMENT), если это согласуется с моделью данных.
Проверяйте планы через EXPLAIN и при возможности EXPLAIN ANALYZE. Признаки Using filesort и Using temporary на больших объёмах — сигнал к пересмотру индекса или переписыванию запроса.
Запросы: маленькие победы без переписывания проекта
- Избегайте
SELECT *в частых путях — выбирайте необходимые столбцы. - Фильтруйте до JOIN — переносите селективные условия в подзапросы/CTE слева.
- Согласуйте
ORDER BYс левой частью индекса и направлением. - Оптимизируйте пагинацию: вместо
OFFSET N LIMIT Mпри больших N используйте keyset pagination. - Разбивайте избыточные OR на UNION ALL, если это улучшает планы.
Транзакции и изоляция
Дефолтная изоляция InnoDB — REPEATABLE READ. Она хороша, но длинные транзакции копят историю (undo) и утяжеляют «чистку». Если приложение допускает, рассмотрите READ COMMITTED — она снижает конкуренцию за версионность. В любом случае:
- держите транзакции короткими;
- избегайте «залипших» транзакций в пулах соединений;
- следите за метриками
Innodb_row_lock_time, ростом undo и долгими блокировками.
Временные таблицы и диск
Большая доля «непонятных тормозов» — это внезапный уход во временные таблицы на диске. Контролируйте Created_tmp_disk_tables и Created_tmp_tables, настраивайте tmp_table_size/max_heap_table_size синхронно, чтобы больше временных таблиц оставались в памяти. Но не раздувайте эти значения бездумно — на малом VDS это риск OOM при всплесках соединений.
Файловая система и слой виртуализации тоже влияют: если ваш VDS на медленном сетевом хранилище, особенно избегайте неиндексируемых сортировок/джойнов. Индексы и снижение объёмов данных — ваш лучший друг.
Бинарный лог и репликация
Если вы не используете репликацию или точечное восстановление, можно держать бинарный лог выключенным — он создаёт накладные расходы на запись. Если он нужен, задайте срок хранения через binlog_expire_logs_seconds и контролируйте объёмы. Не забывайте про sync_binlog — баланс между надёжностью и скоростью.
Пошаговый план тюнинга без риска
- Снимите базовую метрику: пиковое потребление RAM, наличие свопа, латентность диска, QPS/TPS, ключевые
SHOW GLOBAL STATUSи выборки из Performance Schema. - Настройте
innodb_buffer_pool_sizeс запасом для системы и per-connection буферов. Проверьте, что своп не используется в обычном режиме. - Включите slow query log с разумными порогами и прокатайте типичную нагрузку 1–2 дня.
- Отсортируйте «сигнатуры» медленных запросов по общей стоимости. Начните с топ-3 и исправьте индексы/планы.
- Проверьте redo-параметры: нет ли чрезмерных flush; при необходимости плавно увеличьте ёмкость редо-журнала.
- Пересмотрите per-connection буферы и
max_connections, чтобы исключить риск OOM при пиках. - Повторяйте цикл: измерить → изменить одну вещь → проверить → зафиксировать.
Чек-лист стартовой конфигурации для VDS с 1 ГБ RAM
- InnoDB:
innodb_buffer_pool_size~ 600–700 МБ,innodb_buffer_pool_instances=1–2;innodb_flush_log_at_trx_commit=1 (или 2 как компромисс);innodb_flush_method=O_DIRECT (проверить); разумный размер редо. - Соединения:
max_connections=50–100;sort_buffer_size=1–2 МБ;join_buffer_size=1–2 МБ. - Временные таблицы:
tmp_table_size=32–64 МБ,max_heap_table_size=32–64 МБ (равные значения). - Логи: включить slow query log, задать
long_query_time=0.5–1.0, продумать ротацию логов. - Performance Schema: оставить включённым, но без избыточных потребителей; периодически снимать срезы по digest/IO waits.
- Индексы: проверить частые запросы, добавить покрывающие/составные индексы, укрепить селективность, убрать неиспользуемые индексы.
# Пример my.cnf для MySQL 8.0 на VDS с 1 ГБ RAM (базовый старт)
[mysqld]
innodb_buffer_pool_size = 650M
innodb_buffer_pool_instances = 1
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_redo_log_capacity = 512M
max_connections = 80
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 1M
slow_query_log = ON
long_query_time = 0.7
log_queries_not_using_indexes = ON
min_examined_row_limit = 1000
log_slow_admin_statements = ON
# Если нужен binlog
# server_id = 1
# binlog_format = ROW
# sync_binlog = 1
# binlog_expire_logs_seconds = 604800
Частые антипаттерны
- Подняли
max_connectionsдо сотен/тысяч — получили лавину памяти. - Раздули
tmp_table_sizeи per-connection буферы без анализа — OOM при пиках. - Нет составных индексов под реальные фильтры/сортировки — массовые filesort/temporary.
- Длинные транзакции и «висящие» соединения из пула — накапливается undo/версии.
- LIKE с ведущим
%вместо полнотекстового поиска. - UUID как PK без необходимости — рост индексов и фрагментация.
- Нет ротации slow log/binlog — забивка диска.
MariaDB vs MySQL: мелкие различия, важные на практике
- Redo-журналы: MySQL 8.0 —
innodb_redo_log_capacity, MariaDB —innodb_log_file_size/innodb_log_files_in_group. - Функциональные индексы и
EXPLAIN ANALYZE: версии и синтаксис отличаются; проверяйте вашу минорную версию. - Query Cache: отсутствует в MySQL 8.0, в MariaDB обычно выключают на нагруженных системах.
При переносе конфигов между MySQL и MariaDB не делайте механической замены — сверяйте справку по параметрам именно вашей версии.
Заключение
Базовый тюнинг MySQL/MariaDB на малом VDS — это грамотное распределение памяти (в первую очередь innodb_buffer_pool_size), дисциплина в per-connection буферах, включённый slow query log, разумное использование Performance Schema и уверенная работа с индексами. Такой фундамент уменьшает дисковую активность, повышает кэш-хит и даёт чёткий план улучшений. Дальше — итеративная query optimization: улучшаете топ-3 «дорогих» запросов, переснимаете метрики, повторяете цикл.


