Блог компании 3v-Hosting

Как найти и оптимизировать медленные запросы в MySQL

Администрирование

14 мин.


Производительность сайта - это один из ключевых факторов, влияющих не только на пользовательский опыт, но и на бизнес-показатели, такие как конверсию, удержание аудитории и даже позиции в поисковых системах. Современные пользователи не готовы ждать загрузки страницы дольше одной секунды, а поисковые системы всё чаще учитывают реальные метрики скорости при ранжировании сайтов в поисковой выдаче. В последние несколько лет Google прямо указывает в своих документах, что чем быстрее работает сайт, тем выше он ранжируется, и наоборот - если сайт чрезвычайно медлителен, то он и вовсе может не попасть в поисковую выдачу даже по самому релевантному запросу.

При этом распространённая ошибка - считать, что вся проблема кроется исключительно в мощности сервера или ширине канала связи. На практике даже мощный сервер с SSD и большим объёмом оперативной памяти не спасёт сайт, если база данных работает неэффективно. Медленные и неоптимизированные SQL-запросы способны «убить» производительность приложения, вызвать лавинообразный рост нагрузки и привести в итоге к полному падению сервиса.

В этой статье мы разберем пошаговый и безопасный подход к поиску, анализу и оптимизации медленных MySQL запросов, который подходит для реальных production-проектов и актуален для современных версий MySQL и MariaDB.

 

 

 

 

 

Совместимость по версиям MySQL и MariaDB

Прежде чем приступать к оптимизации производительности базы данных, необходимо точно понимать, какая версия СУБД используется на сервере и какие возможности она предоставляет. От версии MySQL или MariaDB напрямую зависит набор доступных инструментов диагностики, поведение оптимизатора запросов, поддержка тех или иных механизмов кэширования, а также корректность и безопасность конфигурационных параметров. Рекомендации, актуальные для старых версий, в современных релизах могут быть неэффективны или вовсе отсутствовать, поэтому учет версии СУБД является обязательным шагом перед началом любых работ по анализу и оптимизации.

В таблице ниже, в наглядном виде, мы собрали основные компоненты СУБД использующиеся для поиска и оптимизации медленных запросов и их поддержку различными версиями СУБД.

 

Таблица - поддержка компонентов различными версиями СУБД

Компонент MySQL 5.7 MySQL 8.0 MariaDB 10.x
Slow Query Log Да Да Да
Performance Schema Да Да Частично
sys schema Да Да Частично
Query Cache Устаревает Удалён Есть (не рекомендован)

Например, начиная с MySQL 8.0, Query Cache полностью удалён, так как он плохо масштабировался и создавал блокировки. Поэтому любые рекомендации по его включению в современных системах являются устаревшими и потенциально вредными.

 

 

 

 

 

Шаг 1. Включение журнала медленных запросов MySQL

Первый и обязательный шаг в поиске проблем с производительностью БД - это включение лога меленных запросов или slow query log. Это механизм MySQL, который фиксирует SQL-запросы, выполняющиеся дольше заданного времени. Именно он позволяет перейти от субъективных ощущений («сайт тормозит») к конкретным фактам.

Для включения этого лога откройте конфигурационный файл вашего экземпляра MySQL. Чаще всего это либо /etc/mysql/mysql.conf.d/mysqld.cnf или /etc/my.cnf. Добавьте в конце этого файла следующие строки:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 2 

 

Дополнительные параметры, которые могут быть полезны:

log_queries_not_using_indexes = 0
min_examined_row_limit = 1000 

 

Пояснения:

  • long_query_time - это порог времени в секундах. Запросы быстрее этого значения не будут логироваться;
  • log_queries_not_using_indexes - включает запись в журнал только тех SQL-запросов, которые выполняются без использования индексов, то есть выполняют полное сканирование таблицы. Поэтому данный параметр стоит включать только временно, иначе лог быстро превратится в мусор;
  • min_examined_row_limit - позволяет игнорировать запросы, работающие с малым числом строк.

 

После изменений перезапустите MySQL с помощью следующей команды:

systemctl restart mysql 

 

Ротация slow log и контроль дискового пространства

На активных и высоконагруженных сайтах файл журнала медленных запросов может очень быстро увеличиваться, особенно при низком пороге long_query_time или большом количестве однотипных запросов. Без настроенной ротации slow log способен за считанные дни занять гигабайты дискового пространства, что приведёт не только к проблемам с логированием, но и к куда более серьёзным последствиям, таким как  переполнению файловой системы, невозможности записи новых данных и, как следствие, аварийной остановке MySQL или всего сервиса целиком.

Именно поэтому slow log следует рассматривать как временный диагностический инструмент, то есть его необходимо регулярно ротировать, контролировать размер файлов и своевременно очищать устаревшие данные, особенно в production-среде с постоянным пользовательским трафиком.

Поэтому рекомендуется:

  • хранить slow log отдельно от системных логов;
  • настраивать регулярную ротацию;
  • ограничивать количество архивов;
  • периодически очищать старые данные.

Важно понимать, что slow log - это инструмент диагностики, а не архив навсегда.

 

 

 

 

Шаг 2. Анализ журнала медленных запросов

Также важно понимать, что slow query log - это не SQL-файл, его нельзя «скормить» MySQL-клиенту. Для анализа используются специальные утилиты, которые умеют агрегировать и интерпретировать собираемые данные. Вот эти утилиты:

 

mysqldumpslow

mysqldumpslow - это встроенный MySQL инструмент, который подходит для быстрой первичной оценки медленных запросов. Например выполнив команду:

mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log 

 

Вы получите:

  • сгруппированные запросы;
  • среднее и максимальное время выполнения;
  • общее количество запусков.

 

Это позволяет быстро понять, какие запросы стоит анализировать в первую очередь.

 

 

pt-query-digest

Когда общая картина становится понятной - нужно провести детальный глубокий анализ по конкретному запросу. Для этого используется pt-query-digest. Он показывает:

  • общий вклад конкретного запроса в нагрузку;
  • распределение времени;
  • частоту выполнения запроса;
  • процент влияния на систему в общем.

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

 

 

 

 

 

Шаг 3. Анализ запроса с помощью EXPLAIN

Когда проблемный запрос уже найден и подтверждён данными из slow query log или Performance Schema, закономерно возникает вопрос - почему именно этот запрос выполняется медленно. Сам по себе факт долгого выполнения ещё не даёт понимания причины, так как это может быть отсутствие индексов, неудачный порядок соединения таблиц, сортировки вне индекса, работа с большим объёмом данных или неоптимальная структура запроса.

Ответ на этот вопрос даёт оператор EXPLAIN, который позволяет заглянуть внутрь механизма выполнения запроса и увидеть, какой план действий выбирает оптимизатор MySQL. С его помощью можно понять, какие таблицы и в каком порядке обрабатываются, используются ли индексы, сколько строк сервер ожидает просканировать и какие дополнительные операции выполняются в процессе. Именно анализ плана выполнения является ключевым этапом при оптимизации медленных запросов, так как он позволяет принимать решения на основе фактов, а не предположений.

Посмотрим на такой пример:

EXPLAIN SELECT * FROM customers WHERE age > 30; 

 

Ключевые поля EXPLAIN и их влияние на производительность

При анализе плана выполнения запроса особое внимание следует уделять нескольким полям вывода EXPLAIN. Именно они дают наиболее точное представление о том, как именно MySQL получает и обрабатывает данные, и где возникают узкие места.

type - способ доступа к данным
Поле type показывает, каким образом MySQL читает данные из таблицы. Это один из самых важных индикаторов производительности запроса. Чем «умнее» способ доступа, тем меньше данных серверу приходится обрабатывать.

Наихудшим вариантом считается значение ALL, которое означает полный перебор таблицы. В этом случае MySQL вынужден прочитать каждую строку, независимо от условий запроса. При больших объёмах данных это приводит к резкому росту времени выполнения и нагрузке на диск и CPU. Более предпочтительные значения - ref, range, const, так как они указывают на использование индексов и работу с ограниченным набором строк.

key - используемый индекс
Поле key показывает, какой именно индекс был выбран оптимизатором для выполнения запроса. Если значение NULL, это означает, что ни один индекс не был использован, даже если он существует. Причины могут быть разными: неподходящая структура запроса, несовпадение типов данных, использование функций в WHERE или неверный порядок условий.

Отсутствие индекса почти всегда означает увеличение времени выполнения запроса, особенно на больших таблицах. Если индекс есть, но не используется, это явный сигнал к пересмотру запроса или схемы индексации.

rows - оценка количества обрабатываемых строк
Поле rows показывает, сколько строк MySQL планирует просканировать для получения результата. Это оценка, а не точное значение, но она позволяет понять масштаб операции. Большие значения rows указывают на то, что серверу приходится обрабатывать значительный объём данных, что напрямую влияет на производительность.

Даже при наличии индекса высокий показатель rows может свидетельствовать о низкой селективности условий или неудачном выборе индекса. В таких случаях оптимизация запроса или создание более подходящего индекса может существенно снизить нагрузку.

Extra - дополнительные операции
Поле Extra содержит дополнительную информацию о действиях, которые MySQL выполняет в процессе обработки запроса. Именно здесь часто скрываются самые неприятные сюрпризы.

Записи вроде Using filesort означают, что сортировка выполняется вне индекса, что может привести к использованию временных файлов и замедлению запроса. Значение Using temporary указывает на создание временных таблиц, которые особенно опасны при работе с большими объёмами данных и сложными GROUP BY или ORDER BY. Такие операции увеличивают нагрузку на память и диск и часто становятся причиной резкого падения производительности.

 

Обобщим опасные признаки, требующие внимания

При анализе EXPLAIN существуют признаки, которые почти всегда указывают на потенциальные проблемы и требуют дальнейшего разбирательства:

  • type = ALL - MySQL выполняет полный перебор таблицы, что критично для больших наборов данных.
  • key = NULL - индекс не используется, даже если он существует, что приводит к лишней обработке строк.
  • Using filesort - сортировка выполняется вне индекса и может задействовать временные файлы.
  • Using temporary - создаются временные таблицы, что увеличивает нагрузку и снижает стабильность под нагрузкой.

Обнаружение одного или нескольких таких признаков не всегда означает катастрофу, но почти всегда служит сигналом к тому, что запрос требует оптимизации или пересмотра структуры данных.

 

Индексация

Индексы - это мощный, но не универсальный инструмент. Ошибочная индексация может ухудшить производительность вашей базы данных.

Основные рекомендации, касаемо построения индексов:

  • индексируйте поля, используемые в WHERE, JOIN, ORDER BY;
  • учитывайте порядок условий в составных индексах;
  • не индексируйте всё подряд;
  • всегда проверяйте эффект через EXPLAIN.

Важно! После добавления индекса, настоятельно рекомендуем вам снова повторить тесты.

 

 

 

 

 

Шаг 4. Использование MySQL Performance Schema

Performance Schema предназначена для глубокого и детального анализа поведения базы данных на уровне внутренних механизмов MySQL. В отличие от slow query log, который фиксирует только отдельные медленные запросы постфактум, Performance Schema работает в реальном времени и постоянно собирает статистику о выполнении запросов, использовании ресурсов, ожиданиях блокировок и других ключевых показателях.

Особенность Performance Schema заключается в том, что она агрегирует данные по типам и шаблонам запросов, а не по конкретным значениям параметров. Это позволяет увидеть не единичные проблемы, а системные паттерны нагрузки, например какие классы запросов создают основную нагрузку на сервер, какие операции чаще всего потребляют CPU или время ожидания, и какие участки приложения требуют оптимизации в первую очередь. Такой подход особенно полезен на высоконагруженных проектах, где множество одинаковых запросов по отдельности выглядят безобидно, но в сумме оказывают серьёзное негативное влияние на производительность базы данных.

Пример запроса к Performance Schema:

SELECT * FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10; 

 

Этот запрос показывает:

  • самые «дорогие» запросы;
  • их суммарное влияние;
  • среднее время выполнения.

 

Важно также помнить, что данные в Performance Schema агрегируются по шаблонам запросов, а не по конкретным значениям.

 

 

 

 

 

Шаг 5. Современное кэширование вместо Query Cache

В современных версиях MySQL повышение производительности достигается не за счёт встроенного Query Cache, который устарел и был удалён из MySQL 8.0, а за счёт многоуровневого кэширования на разных уровнях архитектуры приложения. Такой подход позволяет значительно снизить нагрузку на базу данных и ускорить отклик сайта без усложнения SQL-запросов.

На уровне приложения чаще всего используется кэширование с помощью Redis или Memcached. Эти системы позволяют сохранять результаты часто выполняемых запросов или вычислений в памяти и повторно отдавать их без обращения к базе данных. Особенно эффективно это работает для данных, которые редко изменяются, но часто запрашиваются, например списков, настроек или справочной информации.

На уровне веб-сервера применяется кэширование динамического контента, например с помощью fastcgi_cache в Nginx. Такой подход позволяет отдавать готовые HTML-страницы без запуска PHP или другого бэкенд-приложения, что существенно снижает нагрузку как на веб-сервер, так и на MySQL. Это особенно полезно для страниц с высокой посещаемостью и одинаковым содержимым для большинства пользователей.

Со стороны самой базы данных ключевую роль играет InnoDB Buffer Pool - область памяти, в которой MySQL хранит часто используемые данные и индексы. При достаточном размере buffer pool большинство операций чтения выполняется из оперативной памяти, а не с диска, что многократно ускоряет работу. Грамотная настройка этого параметра зачастую даёт больший прирост производительности, чем любые точечные оптимизации запросов.

Наконец, на уровне кода приложения важно использовать подготовленные выражения (prepared statements). Они позволяют повторно использовать планы выполнения запросов, уменьшая накладные расходы на их разбор и оптимизацию, а также повышают безопасность приложения.

В совокупности такой многоуровневый подход к кэшированию в большинстве случаев даёт более ощутимый и стабильный эффект, чем попытки ускорить базу данных исключительно за счёт переписывания сложных SQL-запросов.

 

 

 

 

 

Проверка результата

Любая оптимизация базы данных имеет смысл только тогда, когда её результат можно чётко зафиксировать и сравнить. Изменения в индексах, запросах или конфигурации MySQL должны приводить к измеримому эффекту, иначе невозможно понять, были ли действия действительно полезными или изменения не дали ожидаемого результата.

Именно поэтому после каждой оптимизации необходимо пройти стандартный цикл проверки, который позволяет объективно оценить влияние внесённых изменений.

 

Обязательные шаги после оптимизации

После внесения изменений рекомендуется выполнить следующий набор действий:

Обновить статистику таблиц
Выполнение команды ANALYZE TABLE заставляет MySQL пересчитать статистику распределения данных. Это особенно важно после добавления или изменения индексов, так как без актуальной статистики оптимизатор может продолжать использовать неоптимальный план выполнения запросов.

 

Сравнить журнал медленных запросов
Необходимо сопоставить slow query log до и после оптимизации. Важно анализировать не только максимальное время выполнения запросов, но и:

  • общее количество медленных запросов;
  • среднее время выполнения;
  • суммарный вклад конкретных запросов в нагрузку на систему.

 

Повторно проанализировать EXPLAIN

Для оптимизированных запросов следует повторно выполнить EXPLAIN и убедиться, что:

  • используются нужные индексы;
  • снизилось ожидаемое количество обрабатываемых строк (rows);
  • исчезли признаки Using temporary и Using filesort, если они были ранее.

 

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

  • инструменты мониторинга;
  • нагрузочное тестирование;
  • логи веб-сервера или APM-системы.

 

Критерии оценки эффективности оптимизации

Для наглядности результаты оптимизации удобно сводить в сравнительную таблицу по примеру этой:

Показатель До оптимизации После оптимизации Комментарий
Количество медленных запросов      
Максимальное время запроса      
Среднее время выполнения      
Использование индексов Нет / Частично Да  
Наличие temporary/filesort Да / Нет Да / Нет  
Среднее время ответа страницы      

Такой подход позволяет не только зафиксировать результат, но и документировать проведённые изменения, что особенно важно для командной работы, поддержки проекта в будущем и повторных оптимизаций.

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

 

 

 

 

 

Часто задаваемые вопросы (FAQ)

Можно ли включать slow query log на продакшене?

Да, журнал медленных запросов можно и нужно использовать на production-серверах, если он настроен корректно. Важно задать разумный порог long_query_time, включить ротацию логов и контролировать размер файлов. При таких условиях slow log практически не влияет на производительность и позволяет выявлять реальные проблемы без риска для стабильности системы.

 

Какой long_query_time лучше использовать?

Для большинства сайтов оптимальным значением является диапазон 1–2 секунды. На высоконагруженных проектах порог можно снизить, но делать это стоит временно и под контролем, иначе журнал будет переполнен малозначимыми запросами.

 

Почему запрос медленный, если для него существует индекс?

Наличие индекса не гарантирует его использование. MySQL может игнорировать индекс из-за неподходящей структуры запроса, использования функций в условиях, несовпадения типов данных, низкой селективности индекса или устаревшей статистики. В таких случаях необходимо анализировать EXPLAIN и при необходимости обновлять статистику или пересматривать сам запрос.

 

Нужно ли всегда добавлять индекс для медленного запроса?

Не всегда. Иногда добавление индекса действительно ускоряет запрос, но в других случаях проблема кроется в логике запроса, лишних соединениях таблиц или избыточной выборке данных. Кроме того, каждый новый индекс увеличивает нагрузку на операции записи, поэтому индексацию следует применять осознанно.

 

Что важнее - slow query log или Performance Schema?

Эти инструменты решают разные задачи и лучше всего работают вместе. Slow query log подходит для анализа конкретных медленных запросов и истории проблем, а Performance Schema позволяет видеть общую картину нагрузки и выявлять системные паттерны в реальном времени.

 

Можно ли оптимизировать базу данных без доступа к продакшену?

Частично да, но эффективность будет ниже. Без реальных данных о нагрузке, частоте запросов и поведении пользователей сложно сделать точные выводы. Для качественной оптимизации желательно иметь доступ к slow log или статистике Performance Schema именно с production-окружения.

 

Как понять, что оптимизация действительно помогла?

Признаками успешной оптимизации являются сокращение количества медленных запросов, снижение среднего и максимального времени выполнения, улучшение планов выполнения EXPLAIN, а также уменьшение времени отклика сайта или приложения для конечных пользователей.

 

 

 

 

 

Заключение

Поиск и оптимизация медленных запросов MySQL - это не разовая задача, а скорее постоянный процесс, который должен стать частью регулярного обслуживания любого сайта или приложения. Даже простые и базовые шаги, такие как включение slow query log, анализ планов выполнения запросов и корректная индексация, позволяют выявить и устранить большинство причин, приводящих к замедлению работы сайта и росту нагрузки на сервер.

Важно помнить, что любые изменения в структуре базы данных или конфигурации MySQL требуют осознанного подхода и обязательной проверки результата. Ошибочные действия, выполненные без понимания последствий, могут не только не дать прироста производительности, но и привести к серьёзным проблемам, например к деградации скорости, потере данных или даже полной недоступности сервиса.

Грамотная оптимизация строится на фактах и измерениях, аккуратных изменениях и регулярном контроле состояния базы данных. Именно такой методичный подход позволяет обеспечить стабильную, предсказуемую и быструю работу MySQL в долгосрочной перспективе.

Как работают IP-адреса?
Как работают IP-адреса?

Подробно о том, как работают IP-адреса, различия IPv4 и IPv6, публичные и приватные IP, DNS, маршрутизация, безопасность и применение в серверной инфраструктуре...

12 мин
Обязательные настройки Nginx для WordPress
Обязательные настройки Nginx для WordPress

Ускорение WordPress на уровне Nginx: правильные настройки PHP-FPM, try_files, статика, кеширование, Brotli, защита wp-login и безопасные заголовки для стабильно...

12 мин