Когда сайт на VDS начинает отвечать медленнее, первым подозреваемым часто становится база данных. Но команда top или график CPU редко отвечают на главный вопрос: какой именно запрос тормозит и почему. Для MySQL и MariaDB один из самых полезных инструментов первичной диагностики — slow query log, журнал медленных запросов.
Slow query log фиксирует SQL-запросы, которые выполнялись дольше заданного порога, а также может записывать запросы без индексов и административные операции. На небольшом сервере это особенно ценно: один неудачный SELECT по большой таблице способен занять CPU, вызвать чтение с диска, задержать PHP-FPM и в итоге превратить нормальный сайт в очередь ожидания.
В этой инструкции разберём, как безопасно включить slow query log в MySQL и MariaDB, какие параметры действительно важны, как не заполнить диск логами, как ротировать файл и как анализировать результат через pt-query-digest. Материал рассчитан на администраторов, devops-инженеров и вебмастеров, которые обслуживают LAMP/LEMP-стек.
Что показывает slow query log и чего он не показывает
Slow query log — это не APM и не профилировщик всего приложения. Он не расскажет, почему пользовательская сессия ждала внешний API, не покажет медленный шаблонизатор и не объяснит блокировку в коде PHP. Зато он хорошо отвечает на вопросы уровня SQL:
- какие запросы выполняются дольше всего;
- какие запросы чаще всего повторяются;
- сколько строк просматривает запрос и сколько возвращает;
- есть ли ожидание блокировок;
- какие шаблоны запросов создают наибольшую суммарную нагрузку;
- появились ли регрессии после релиза, миграции или изменения индекса.
Главная польза slow query log — не в единичной строке лога, а в группировке одинаковых запросов. Один запрос на 8 секунд неприятен, но запрос на 120 мс, который выполняется 50 тысяч раз в час, может быть гораздо опаснее.
Поэтому журнал почти всегда стоит анализировать не глазами, а агрегатором. В статье для этого используется pt-query-digest из Percona Toolkit: он группирует похожие запросы, считает процентили, суммарное время, среднее число просмотренных строк и показывает примеры.
Быстрая проверка текущих настроек MySQL или MariaDB
Сначала подключитесь к серверу базы данных под административным пользователем. Это может быть root через Unix-сокет, отдельный DBA-пользователь или доступ через панель, если она управляет MySQL/MariaDB. Внутри клиента выполните:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'slow_query_log_file';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
SHOW VARIABLES LIKE 'min_examined_row_limit';
Если вы не уверены, что перед вами MySQL или MariaDB, проверьте версию:
SELECT VERSION();
В типичной установке slow query log выключен, long_query_time равен 10 секундам, а файл журнала находится внутри каталога данных MySQL. Такой порог слишком высокий для большинства веб-проектов: запросы по 2–3 секунды уже заметны пользователю, а на API даже 300–500 мс могут быть проблемой.
Но не спешите сразу ставить long_query_time в 0. На живом VDS это может создать большой поток логов и дополнительную нагрузку на диск. Лучше начинать с разумного порога и снижать его короткими диагностическими окнами.

Как включить slow query log без перезапуска
Для первичной диагностики удобно включить журнал динамически. Настройки применятся сразу, но после перезапуска MySQL/MariaDB часть из них может сброситься, если вы не добавите параметры в конфигурационный файл.
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL min_examined_row_limit = 1000;
SET GLOBAL log_queries_not_using_indexes = 'OFF';
Такой набор означает: записывать запросы дольше 1 секунды, но не шуметь из-за совсем маленьких таблиц. Параметр min_examined_row_limit полезен на CMS и фреймворках, где много коротких служебных запросов: он позволяет не засорять лог запросами, которые просмотрели десятки строк и не представляют интереса.
Путь к файлу можно задать отдельно:
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
Перед этим убедитесь, что каталог существует и доступен пользователю, от которого запущен сервер базы данных. На Debian/Ubuntu это обычно пользователь mysql. На RHEL-based системах чаще используется тот же пользователь, но путь к логам и политика SELinux могут отличаться.
Проверить, что запись действительно идёт, можно простым тестом:
SELECT SLEEP(2);
После этого посмотрите последние строки файла:
tail -n 50 /var/log/mysql/mysql-slow.log
Если файл пустой, проверьте три вещи: включён ли slow_query_log, совпадает ли путь в slow_query_log_file, есть ли права на запись в каталог. На системах с SELinux дополнительно проверьте контекст файла и сообщения аудита.
Постоянная настройка через конфигурационные файлы
Динамическое включение удобно для аварийной диагностики, но постоянные настройки лучше хранить в конфиге. Так вы не потеряете их после обновления, перезапуска службы или миграции.
Debian и Ubuntu
На Debian/Ubuntu настройки обычно добавляют в файл /etc/mysql/mysql.conf.d/mysqld.cnf для MySQL или в один из файлов каталога /etc/mysql/mariadb.conf.d/ для MariaDB. Важно размещать параметры в секции [mysqld].
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
min_examined_row_limit = 1000
log_queries_not_using_indexes = OFF
AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux
В RHEL-based дистрибутивах часто используют файлы в каталоге /etc/my.cnf.d/. Например, можно создать отдельный файл /etc/my.cnf.d/slow-query.cnf, чтобы не смешивать диагностику с базовыми настройками сервера.
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
min_examined_row_limit = 1000
log_queries_not_using_indexes = OFF
После изменения конфигурации проверьте синтаксис косвенно через перезапуск в плановое окно. На продуктивном сервере сначала убедитесь, что у вас есть свежий бэкап и доступ в консоль VDS на случай ошибки в конфиге.
systemctl restart mysql
Для MariaDB служба часто называется иначе:
systemctl restart mariadb
После перезапуска проверьте переменные повторно:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'slow_query_log_file';
SHOW VARIABLES LIKE 'long_query_time';
Как выбрать long_query_time для VDS
Значение long_query_time зависит от профиля проекта. Для административной базы отчётов запрос на 2 секунды может быть нормальным. Для интернет-магазина или API, который участвует в генерации страницы, это уже много. На VDS важно учитывать не только абсолютное время запроса, но и конкуренцию за ресурсы: CPU, диск, память, размер буферного пула InnoDB.
Практичный подход такой:
2секунды — безопасный старт для первого включения на неизвестной нагрузке;1секунда — нормальный рабочий порог для большинства сайтов;0.5секунды — полезно для оптимизации динамических страниц и API;0.1секунды и ниже — только короткими окнами, если вы понимаете объём логов.
Если сайт уже испытывает проблемы, начните с 1 или 2 секунд на 15–30 минут. Этого часто достаточно, чтобы поймать самые тяжёлые запросы. Когда грубые проблемы исправлены, можно временно снизить порог и искать менее заметные, но частые запросы.
Не включайте агрессивный slow log навсегда без ротации и контроля размера. Диагностический инструмент не должен становиться причиной заполнения диска.
Записывать ли запросы без индексов
Параметр log_queries_not_using_indexes выглядит соблазнительно: включил — и база сама покажет плохие запросы. На практике он часто создаёт шум. Некоторые запросы без индекса нормальны: маленькие справочники, временные таблицы, выборки по нескольким десяткам строк. В CMS можно получить тысячи записей, которые не имеют отношения к настоящей проблеме.
Если хотите использовать этот режим, включайте его коротко и обязательно вместе с min_examined_row_limit:
SET GLOBAL log_queries_not_using_indexes = 'ON';
SET GLOBAL min_examined_row_limit = 10000;
После диагностического окна выключите:
SET GLOBAL log_queries_not_using_indexes = 'OFF';
В MariaDB есть дополнительные возможности детализации slow log, например log_slow_verbosity. Они полезны при глубокой диагностике, но для первого прохода лучше не перегружать журнал: начните с базовых полей, агрегируйте через pt-query-digest, а затем включайте расширенную детализацию точечно.
Права на файл и каталог логов
Распространённая ошибка — указать удобный путь к slow log, но забыть выдать права. Создайте каталог, если его нет, и назначьте владельца. Для Debian/Ubuntu и большинства RHEL-based установок подходит:
mkdir -p /var/log/mysql
chown mysql:mysql /var/log/mysql
chmod 750 /var/log/mysql
Если служба запущена не от пользователя mysql, проверьте фактического пользователя:
ps -o user,group,comm -C mysqld
ps -o user,group,comm -C mariadbd
На системах с SELinux одного chown может быть недостаточно. Если MySQL/MariaDB не пишет в файл, проверьте журнал аудита и контексты. Иногда проще использовать стандартный каталог логов, уже предусмотренный пакетной установкой, чем заводить новый путь с нестандартным контекстом.
Ротация slow query log
Slow query log может расти очень быстро. На VDS с небольшим диском это критично: заполненный /var способен остановить запись бинарных логов, временных файлов, почтовой очереди и системных журналов. Поэтому ротацию нужно настроить сразу, особенно если long_query_time меньше 1 секунды.
Простой вариант — использовать logrotate. Пример конфигурации:
/var/log/mysql/mysql-slow.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 640 mysql mysql
postrotate
/usr/bin/mysqladmin --defaults-extra-file=/root/.mysql-logrotate.cnf flush-logs
endscript
}
У этого примера есть важный нюанс: mysqladmin должен иметь право выполнить сброс логов. Для аккуратной настройки создают отдельный локальный пользователь с минимальной привилегией RELOAD и файл клиента с правами только для root.
CREATE USER 'logrotate'@'localhost' IDENTIFIED BY 'strong_local_password';
GRANT RELOAD ON *.* TO 'logrotate'@'localhost';
FLUSH PRIVILEGES;
[client]
user=logrotate
password=strong_local_password
host=localhost
chmod 600 /root/.mysql-logrotate.cnf
Если не хотите хранить пароль для автоматической ротации, можно использовать copytruncate, но это компромисс: во время копирования и обрезки часть строк теоретически может потеряться. Для диагностического slow log это часто допустимо, для строгого аудита — нет.
Если на сервере важны восстановление на точку времени и работа с бинарными логами, полезно отдельно разобраться с PITR в MySQL и MariaDB через binlog и GTID: slow log и binlog решают разные задачи, но оба требуют контроля места на диске.
Установка pt-query-digest
pt-query-digest входит в Percona Toolkit. Инструмент читает slow query log, группирует запросы по fingerprint и показывает, какие шаблоны дают основную нагрузку. Устанавливайте его на том же VDS или на отдельной машине, куда вы копируете лог для анализа.
Debian и Ubuntu
apt update
apt install percona-toolkit
AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux
dnf install epel-release
dnf install percona-toolkit
Fedora
dnf install percona-toolkit
Если пакет недоступен в подключённых репозиториях, используйте официальный репозиторий поставщика или установочный пакет, принятый в вашей инфраструктуре. Важно не ставить случайные бинарники с непроверенных источников: slow log может содержать имена таблиц, фрагменты запросов и иногда чувствительные значения.
Проверьте установку:
pt-query-digest --version
Первый анализ slow query log
Базовый запуск выглядит так:
pt-query-digest /var/log/mysql/mysql-slow.log
Если файл большой, сначала сохраните отчёт:
pt-query-digest /var/log/mysql/mysql-slow.log --output report
Для анализа за конкретное окно удобнее работать с отдельным фрагментом: включить slow log, подождать 20 минут пиковой нагрузки, выполнить FLUSH SLOW LOGS или скопировать текущий файл, затем разобрать только этот период. Так отчёт будет отражать реальную проблему, а не смесь событий за месяц.
В отчёте pt-query-digest обратите внимание на несколько блоков.
Rank— рейтинг шаблонов запросов по выбранной метрике, обычно по суммарному времени.Query ID— идентификатор fingerprint, то есть нормализованного запроса без конкретных значений.Response time— суммарное время, среднее, минимум, максимум и распределение.Calls— сколько раз встречался запрос.R/Call— среднее время одного выполнения.Rows examineиRows sent— сколько строк просмотрено и сколько возвращено.
Очень часто проблема видна по соотношению: запрос возвращает 20 строк, но просматривает миллионы. Это почти всегда повод проверить индекс, условие WHERE, сортировку ORDER BY или пагинацию.

Как читать найденный запрос
После того как pt-query-digest показал подозрительный fingerprint, возьмите пример запроса из отчёта и проверьте его план. Для MySQL 8 и современных MariaDB полезны EXPLAIN и, если допустимо на тестовой среде, EXPLAIN ANALYZE.
EXPLAIN SELECT id, title, created_at FROM posts WHERE status = 'published' ORDER BY created_at DESC LIMIT 20;
На тестовой базе можно выполнить:
EXPLAIN ANALYZE SELECT id, title, created_at FROM posts WHERE status = 'published' ORDER BY created_at DESC LIMIT 20;
Ищите признаки проблем:
- полное сканирование большой таблицы вместо использования индекса;
Using temporaryиUsing filesortна частом запросе;- слишком большое число просмотренных строк;
- несовпадение порядка колонок в индексе с условиями фильтрации и сортировки;
- функции над индексируемой колонкой, из-за которых индекс не используется;
- неявные преобразования типов, например сравнение числового поля со строкой.
Для примера выше часто помогает составной индекс:
CREATE INDEX idx_posts_status_created_at ON posts (status, created_at);
Но индексы нельзя добавлять автоматически по каждому медленному запросу. Индекс ускоряет чтение, но замедляет запись и занимает место. На VDS это особенно заметно: лишние индексы увеличивают размер таблиц, нагрузку на буферный пул и время бэкапов.
Типовые причины медленных запросов
N+1 запросы из приложения
В slow query log они выглядят как множество одинаковых быстрых или умеренно медленных запросов. Каждый отдельный запрос может занимать 20 мс, но суммарно страница делает сотни обращений. pt-query-digest хорошо показывает такую проблему по высокому Calls и большой суммарной доле времени.
Пагинация через большой OFFSET
Запросы вида LIMIT 50 OFFSET 500000 часто вынуждают базу пройти огромный объём строк, чтобы вернуть маленький фрагмент. Для больших списков лучше использовать keyset pagination: запоминать последний ключ и выбирать следующую страницу по условию.
SELECT id, created_at FROM orders WHERE created_at < '2026-01-01 00:00:00' ORDER BY created_at DESC LIMIT 50;
Сортировка без подходящего индекса
Если запрос фильтрует по одной колонке, а сортирует по другой, одиночные индексы могут не помочь. Часто нужен составной индекс, соответствующий реальному шаблону запроса. Проверяйте это через EXPLAIN, а не по интуиции.
Низкая селективность условия
Индекс по колонке is_active с двумя значениями редко полезен сам по себе. Но он может быть частью составного индекса, если дальше идёт более селективная колонка или сортировка.
Блокировки
Если в slow log заметен высокий Lock_time, запрос не обязательно медленный из-за плохого плана. Он мог ждать транзакцию, миграцию, долгий ALTER TABLE или обновление той же строки. В таком случае дополнительно смотрите активные транзакции, metadata locks и статус InnoDB.
Особенности MySQL и MariaDB
Базовые параметры slow_query_log, slow_query_log_file, long_query_time и log_queries_not_using_indexes есть и в MySQL, и в MariaDB. Но дополнительные опции могут отличаться по названию и поведению. Например, MariaDB развивала собственные параметры детализации slow log, а MySQL 8 активно использует Performance Schema и sys schema для дополнительной аналитики.
Поэтому правило простое: для переносимой настройки используйте базовый набор, а расширенные параметры проверяйте командой:
SHOW VARIABLES LIKE 'log_slow%';
SHOW VARIABLES LIKE 'slow%';
Не копируйте конфигурацию с чужого сервера без проверки версии. Параметр, который работает в MariaDB, может не существовать в MySQL, и наоборот. В лучшем случае сервер проигнорирует его с предупреждением, в худшем — не запустится после перезапуска.
Безопасность и приватность slow log
Slow query log — технический журнал, но в нём могут оказаться чувствительные данные. Всё зависит от приложения и драйвера: иногда значения параметров логируются прямо в SQL. Это могут быть email, телефоны, токены, части поисковых запросов, внутренние идентификаторы заказов.
Практические меры:
- не выдавайте доступ к slow log всем пользователям системы;
- храните файл с правами вроде
640и владельцемmysql; - не отправляйте необработанный лог в публичные тикеты и чаты;
- перед передачей разработчикам очищайте чувствительные значения;
- задавайте срок хранения через
logrotate; - не включайте слишком подробное логирование без необходимости.
Если вы анализируете проблему совместно с подрядчиком, чаще достаточно отчёта pt-query-digest, а не полного raw-лога. Он нормализует запросы и обычно снижает риск утечки конкретных значений.
Рабочий сценарий диагностики на VDS
Ниже — практичный сценарий, который хорошо подходит для небольшого и среднего проекта на VDS.
- Зафиксируйте симптомы: время, URL, рост CPU, iowait, число соединений MySQL.
- Включите slow query log с
long_query_timeравным1или2. - Соберите лог во время реальной нагрузки 15–30 минут.
- Разберите файл через
pt-query-digest. - Выберите 3–5 запросов с наибольшим суммарным временем.
- Проверьте планы выполнения через
EXPLAINна тестовой базе. - Согласуйте исправление: индекс, изменение запроса, кеширование, устранение N+1.
- Примените изменение в контролируемое окно.
- Повторите сбор slow log и сравните отчёт до и после.
Сравнение до и после — важная часть. Иногда индекс ускоряет один запрос, но ухудшает массовые вставки. Иногда переписанный запрос становится быстрее на малой выборке, но хуже на большой. Slow query log помогает проверять оптимизацию фактами.
Как не перепутать проблему SQL с нехваткой ресурсов
Медленный запрос не всегда означает плохой запрос. На VDS база может тормозить из-за нехватки RAM, активного swap, высокого iowait, заполненного диска, конкуренции с бэкапом или соседними процессами. Поэтому slow log полезно смотреть вместе с системными метриками.
Минимальный набор команд для быстрой проверки:
free -h
vmstat 1 5
iostat -xz 1 5
df -h
ss -tan state established
Если iostat показывает высокую задержку диска, а slow log внезапно заполнился запросами, которые раньше были быстрыми, возможно, проблема не в индексах. Проверьте бэкапы, тяжёлые cron-задачи, бинарные логи, временные таблицы и место в tmpdir.
Если сервер активно уходит в swap, MySQL/MariaDB может резко деградировать даже на простых запросах. В таком случае оптимизация SQL поможет ограниченно: сначала нужно разобраться с памятью, размером innodb_buffer_pool_size, числом соединений и процессами приложения.
Для проектов, где узким местом становится не один запрос, а высокая конкуренция потоков MariaDB, стоит также посмотреть материал про настройку MariaDB Thread Pool. Это уже следующий уровень диагностики после того, как slow log показал реальные шаблоны нагрузки.
Частые ошибки при работе со slow query log
- Оставить
long_query_time = 0на сутки и заполнить диск. - Смотреть только самый долгий единичный запрос и игнорировать частые запросы.
- Добавлять индексы без проверки
EXPLAINи влияния на записи. - Забыть, что slow log после перезапуска отключится, если настройка была только через
SET GLOBAL. - Хранить slow log месяцами без ротации и ограничения доступа.
- Анализировать лог за слишком длинный период, где смешались релизы, бэкапы и ночные задачи.
- Сравнивать отчёты до и после на разной нагрузке и делать неверные выводы.
Хорошая диагностика — это короткое окно наблюдения, понятная гипотеза, измеримое изменение и повторная проверка. Slow query log отлично вписывается именно в такой цикл.
Краткий чек-лист
- Проверьте текущие значения
slow_query_log,slow_query_log_file,long_query_time. - Для первого запуска поставьте
long_query_timeв диапазоне 1–2 секунды. - Не включайте
log_queries_not_using_indexesнадолго безmin_examined_row_limit. - Сразу настройте ротацию slow log.
- Анализируйте файл через
pt-query-digest, а не вручную. - Оптимизируйте запросы по суммарному влиянию, а не только по максимальному времени.
- Проверяйте планы выполнения и тестируйте индексы на копии данных.
- Сравнивайте отчёты до и после изменений.
Slow query log в MySQL и MariaDB — простой, но очень эффективный инструмент для VDS. Он не заменяет мониторинг и профилирование приложения, зато быстро показывает, какие SQL-запросы действительно мешают сайту работать стабильно. Если включать его аккуратно, ротировать логи и разбирать отчёты через pt-query-digest, оптимизация базы становится не угадыванием, а последовательной инженерной задачей.


