Медленные запросы в MySQL редко «живут» по одному: чаще это несколько типовых паттернов (одинаковая форма запроса с разными параметрами), которые постепенно разгоняют нагрузку на CPU/IO и увеличивают время ответа. Правильный подход — не вылавливать единичные запросы вручную, а включить slow query log, собрать статистику за репрезентативное окно и сделать digest-анализ: сгруппировать похожие запросы, чтобы понять, что оптимизировать в первую очередь.
В этой инструкции разберём два практичных источника правды:
MySQL slow query log — фиксирует «медленные» запросы по порогу и дополнительные события (например, неиспользование индексов).
performance_schema — даёт агрегированную статистику внутри сервера, помогает проверять гипотезы и смотреть разрезы по событиям/стейтментам.
А для приоритезации будем использовать pt-query-digest (Percona Toolkit) — он превращает «сырые» записи slow log в понятный рейтинг по суммарному времени, количеству, перцентилям и «эффективности» чтения строк.
Когда slow query log действительно полезен (и когда нет)
Slow log хорошо работает, когда нужно быстро увидеть реальных «виновников» задержек и оценить их вклад в общее время выполнения запросов.
Нужно найти запросы, которые чаще всего создают задержки, и понять их долю в суммарном времени.
Есть подозрение на регресс после релиза: «раньше было быстрее».
Нужно оценить эффект индекса/переписывания: сравнение «до/после» на одинаковом окне.
Но бывают ситуации, где slow log даст неполную картину:
Запросы быстрые, но их слишком много (QPS высокий, latency низкая): упереться можно в конкуренцию, сеть, пул соединений, fsync, буфер-пул, фоновые задачи.
Проблема не в SQL, а в инфраструктуре: IOPS просел, место заканчивается, начался своп, throttling, выросли сетевые задержки.
Практика: начинайте со slow log, потому что он быстро даёт «ТОП-10 виновников». Затем подтверждайте выводы через
performance_schemaи план выполнения (EXPLAIN/EXPLAIN ANALYZE), чтобы не лечить симптомы.
Включаем MySQL slow query log безопасно
Само включение slow log обычно не «ломает» прод, но важно выбрать порог, формат и продумать ротацию, иначе вы быстро упрётесь в размер файла и лишний IO.
Проверяем текущие значения
mysql -e "SHOW VARIABLES LIKE 'slow_query_log%';"
mysql -e "SHOW VARIABLES LIKE 'long_query_time';"
mysql -e "SHOW VARIABLES LIKE 'log_output';"
Ключевые переменные:
slow_query_log— включён ли сбор.slow_query_log_file— путь к файлу.long_query_time— порог «медленности» в секундах (дробные значения допустимы).log_output— куда писать (FILE, TABLE). Для продакшна чаще всего разумнееFILE.
Включаем на лету (временная настройка)
Если нужно быстро собрать диагностику на 15–60 минут, включайте динамически:
mysql -e "SET GLOBAL log_output = 'FILE';"
mysql -e "SET GLOBAL slow_query_log = 'ON';"
mysql -e "SET GLOBAL long_query_time = 0.2;"
0.2 секунды — пример. Для нагруженных систем часто начинают с 0.5–1, чтобы не утонуть в объёме. Для «ловли» коротких подвисаний можно временно опускать до 0.05–0.1, но обязательно контролировать размер файла.
Фиксируем в конфиге (постоянно)
Для постоянного сбора задайте параметры в конфиге MySQL (расположение зависит от дистрибутива):
[mysqld]
slow_query_log=ON
slow_query_log_file=/var/log/mysql/mysql-slow.log
long_query_time=0.2
log_output=FILE
Если меняли только динамические параметры — перезапуск не обязателен. Но путь к файлу, права на каталог и SELinux/AppArmor (если применимо) должны быть корректными, иначе лог просто не начнёт писаться.
Что делать с log_queries_not_using_indexes
Параметр log_queries_not_using_indexes кажется «волшебной кнопкой», но часто превращается в шум: маленькие таблицы и справочники могут читаться полным сканом вполне нормально, а оптимизатор иногда осознанно выбирает non-index plan.
Рекомендация: включайте опцию только на короткое окно и лучше вместе с более высоким порогом long_query_time.
mysql -e "SET GLOBAL log_queries_not_using_indexes = 'ON';"
mysql -e "SET GLOBAL long_query_time = 0.5;"

Ротация slow log без боли
Главная операционная проблема slow log — размер и сопровождение. Если файл растёт без контроля, вы упрётесь в занятый диск или получите неудобный анализ из-за десятков гигабайт.
Базовый принцип: ротация должна сопровождаться переподключением MySQL к файлу логов через FLUSH SLOW LOGS. Если просто переименовать файл, mysqld может продолжить писать в старый файловый дескриптор.
Минимальный ручной сценарий
mv /var/log/mysql/mysql-slow.log /var/log/mysql/mysql-slow.log.1
mysql -e "FLUSH SLOW LOGS;"
gzip -f /var/log/mysql/mysql-slow.log.1
В продакшне лучше использовать системную ротацию, но логика остаётся той же: после rotate выполняйте FLUSH SLOW LOGS.
Digest-анализ: зачем он нужен и как его читать
Сырые записи slow log неудобны: там много повторов одного и того же запроса с разными значениями. Digest — это нормализованная форма SQL, где константы заменяются плейсхолдерами. Например, два запроса:
SELECT * FROM orders WHERE id = 123SELECT * FROM orders WHERE id = 456
В digest-логике это один шаблон, который оптимизируется один раз (индекс, переписывание запроса, изменение схемы).
Устанавливаем pt-query-digest
На Debian/Ubuntu Percona Toolkit часто доступен из стандартных репозиториев дистрибутива или из репозиториев вендора (названия и доступность пакета зависят от ОС и версии).
apt-get update
apt-get install -y percona-toolkit
На RHEL-подобных системах:
yum install -y percona-toolkit
Если пакет недоступен, ставьте именно пакетным способом под вашу ОС (так проще обновлять и откатывать), а не копируйте скрипты вручную на прод.
Строим отчёт по slow log
Самый частый сценарий — получить общий отчёт за период:
pt-query-digest /var/log/mysql/mysql-slow.log > /root/slow-report.txt
В отчёте удобно смотреть на несколько метрик одновременно:
Total — суммарное время, которое «съел» шаблон запроса (часто важнее максимальной задержки).
Count — частота. Частые «слегка медленные» запросы иногда вреднее редких «очень медленных».
95% / stddev — хвосты распределения и разброс. Большой 95-й перцентиль обычно означает пики и нестабильность.
Rows examine / Rows sent — грубый индикатор неэффективности (много читаем, мало отдаём).
Ограничиваем окно по времени
Если лог большой, анализируйте конкретный инцидентный период:
pt-query-digest --since '2025-12-25 10:00:00' --until '2025-12-25 11:00:00' /var/log/mysql/mysql-slow.log > /root/slow-incident.txt
Как перейти от digest к конкретному SQL
Берите самый дорогой digest по Total time или по комбинации time+count.
Ищите в коде приложения соответствующий запрос по форме (обычно он узнаваем).
Воспроизводите на стенде и смотрите
EXPLAIN/EXPLAIN ANALYZE(зависит от версии MySQL).Вносите одно изменение за раз (индекс или переписывание), затем повторяйте сбор и сравнивайте «до/после».
Performance Schema: когда slow log недостаточно
performance_schema — встроенный инструмент MySQL для сбора статистики по событиям (ожидания, стадии, SQL statements). В отличие от slow log, он показывает агрегированную картину даже по быстрым запросам и помогает понять «среднюю температуру» нагрузки.
Проверяем, включен ли performance_schema
mysql -e "SHOW VARIABLES LIKE 'performance_schema';"
Если выключен, включение обычно требует изменения конфигурации и перезапуска:
[mysqld]
performance_schema=ON
Дальше проверьте, что у вас включены нужные инструменты и потребители. Включать «всё подряд» не всегда разумно: это может дать лишние накладные расходы и объём данных.
Быстрый рейтинг запросов по суммарному времени
В MySQL 8 удобно начать с таблиц сводной статистики по statements. Пример запроса (названия колонок могут отличаться между версиями):
mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT, AVG_TIMER_WAIT FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;"
Это digest на стороне MySQL: нормализованный текст, количество выполнений, суммарное и среднее время. Плюс — не нужен файл логов. Минус — нужно контролировать окно наблюдения и понимать, когда статистика сбрасывалась.
Сбрасываем статистику перед замером
Чтобы корректно сравнить «до/после» оптимизации, удобно сбрасывать summary-таблицы на тестовом окне (и осознавать последствия для наблюдаемости):
mysql -e "TRUNCATE TABLE performance_schema.events_statements_summary_by_digest;"
Если вы строите отказоустойчивую схему (репликация, переключения, раздельные роли), заранее продумайте, где именно включать диагностику и как собирать логи с нужного узла. По теме переключений может пригодиться материал про failover на GTID и semi-sync в MySQL.
Типовой рабочий процесс: от инцидента к фиксу
Определите окно проблемы: когда начались задержки, как долго длились, были ли релизы/миграции.
Включите slow log на время окна (или анализируйте уже собранный), выставьте
long_query_timeтак, чтобы получить управляемый объём.Соберите digest-отчёт через
pt-query-digestи выберите 3–5 лидеров по суммарному времени.Подтвердите через performance_schema, что этот digest действительно доминирует (особенно если slow log включали кратко).
Сделайте EXPLAIN на типичных параметрах, проверьте индексы, селективность и сортировки.
Внесите одно изменение за раз и повторите замер «до/после» на сопоставимом окне.

На что смотреть в самом slow log
Даже если вы работаете через digest, полезно понимать базовые поля записи, чтобы не пропустить важные симптомы:
Query_time — время выполнения.
Lock_time — время ожидания блокировок. Если оно большое, причина может быть в конкуренции транзакций, а не в индексе.
Rows_examined — сколько строк просмотрели.
Rows_sent — сколько строк отдали.
Если Lock_time заметно растёт, не спешите добавлять индексы: сначала проверьте длительные транзакции, шаблоны обновлений и конкурирующие запросы. Это часто даёт больший эффект, чем «индекс на всякий случай».
Частые ошибки при включении и анализе
Слишком низкий порог long_query_time навсегда
Порог 0.01 секунды на постоянной основе почти гарантированно создаст огромный лог и дополнительную нагрузку на IO. Делайте так только на короткий период и под контроль диска.
Анализ по «самому медленному запросу», а не по суммарному времени
Один запрос на 20 секунд раз в сутки может быть неприятным, но тысяча запросов по 200 мс каждую минуту убьёт latency намного сильнее. В отчётах всегда оценивайте общий вклад (Total time).
Смешивание разных нагрузочных периодов
Если прогнать digest по логам за неделю, в топ может попасть ночная пакетная задача, а ваша дневная проблема будет спрятана ниже. Отделяйте окна: рабочие часы, ночные джобы, периоды релизов.
Оптимизация без понимания данных
Индекс «по колонке» часто не решает проблему, если условие не селективно или если запрос упирается в сортировку/группировку по другому полю. Всегда смотрите план выполнения и оценку кардинальности.
Мини-чеклист: что подготовить заранее
Выделенный каталог под логи MySQL с понятными правами и мониторингом места.
Ротация slow log и проверка, что
FLUSH SLOW LOGSвызывается после ротации.pt-query-digestна сервере или на отдельной админ-машине для анализа.performance_schemaвключён (если возможно) и вы знаете, как быстро снять топ по digest.
Итог
MySQL slow query log — быстрый способ получить список реальных проблемных запросов на продакшне. pt-query-digest превращает «шумный» лог в понятный digest-рейтинг и помогает выбрать, что чинить первым. А performance_schema дополняет картину: показывает агрегаты на стороне сервера и помогает отличить «медленные запросы» от «ожиданий и конкуренции».
Если выстроить простой процесс «включили лог → сделали digest → подтвердили через performance_schema → починили → сравнили до/после», оптимизация MySQL превращается в повторяемую операционную процедуру, а не в гадание.
На системах, где важна восстановимость и расследование инцидентов, полезно заранее продумать стратегию бэкапов и точку восстановления. По теме может пригодиться заметка про PITR в MySQL/MariaDB через binlog и GTID.
Если вы выбираете площадку под MySQL, обращайте внимание на предсказуемый диск и возможность быстро включать диагностику без сюрпризов по ресурсам — на VDS это обычно проще контролировать, чем в сильно ограниченных окружениях.


