Alex.Miller
New member
Привет, форумчане! Хочу поделиться опытом, который сэкономил мне не один рабочий вечер. Раньше при жалобе «страница грузится десять секунд» я лез в код, крутил слой доступа к данным, добавлял кеширование и всё равно угадывал. Пока не выработал привычку: сначала план запроса, потом всё остальное. Сегодня расскажу, как я укладываюсь в четверть часа, и вам советую попробовать.
Начинается всё с того, что я не верю своим догадкам. В большинстве баз, а я чаще всего работаю с PostgreSQL и MySQL, план достаётся парой команд: один вариант для прикидки и второй с фактическим выполнением, когда нужны реальные цифры. Важный момент: первый показывает предположение планировщика, а он ошибается куда чаще, чем нам кажется. Поэтому на реальных данных я почти всегда прошу показать фактические показатели, но делаю это только на реплике, чтобы не ловить блокировки и не тормозить прод.
Дальше самое ценное правило, которое меня переучило: план читают не сверху вниз, как текст, а от самых вложенных узлов к внешним. В том же PostgreSQL вложенность задаётся отступами, и сердце проблемы почти всегда живёт в глубине дерева. Я переключаю глаза в режим сканера: ищу узел с самой большой стоимостью и самым сильным расхождением между ожидаемым и фактическим числом строк. Уже на этом шаге пять минут приносят первый результат.
Ключевая метрика для меня — это пара «ожидалось и получилось». Когда планировщик думает, что вернётся десять строк, а приходит двести тысяч, он выбирает неправильный способ соединения или теряет индекс, и любой дальнейший анализ становится бессмысленным. Такое расхождение почти всегда означает устаревшую статистику, неудачное выражение в условии или приведение типа, из-за которого индекс перестаёт применяться. Просто обновив статистику, я не раз сокращал время запроса в десятки раз.
Отдельно смотрю на типы доступа к таблицам. Полное последовательное сканирование — вовсе не приговор: если таблица маленькая или выгружается большая часть строк, это самый дешёвый путь. Плохо другое: тяжёлое сканирование огромной таблицы там, где должно быть точечное чтение по индексу. Ещё обращаю внимание на повторные циклы у вложенного цикла, на промежуточную сортировку и на то, не переполняется ли память и не сбрасываются ли временные файлы на диск. Диск в плане — это почти всегда красный флаг.
Мой личный алгоритм выглядит так. Первая минута — беру медленный запрос и запускаю его с фактическими показателями на копии данных. Следующие несколько минут — читаю дерево снизу вверх и отмечаю самый дорогой узел. Дальше — сравниваю ожидаемые и фактические строки, проверяю статистику и условия в фильтрах. Последние минуты — формулирую гипотезу и проверяю её одним изменением: новый индекс, переписанное условие, убранное приведение типов. Правило простое: меняю по одному фактору за раз, иначе не пойму, что именно сработало.
Из инструментов мне хватает встроенных средств клиента: визуализатор плана в административной панели, вывод в текстовом или древовидном формате, расширение для автоматического логирования медленных планов и сбор статистики по запросам. Внешние сервисы выручают, когда дерево большое и хочется увидеть его целиком, но строить на них весь анализ не обязательно. Гораздо важнее привычка записывать: что было до и что стало после, с какими параметрами, на каком объёме данных.
Главный вывод, к которому я пришёл за несколько лет: план запроса — это не страшная простыня текста, а честный отчёт о том, что база решила сделать. Пятнадцать минут внимательного чтения обычно дешевле, чем неделя оптимизации наугад. Расскажите, а как вы ищете узкие места: читаете дерево плана вручную, доверяете визуализаторам или сразу смотрите в мониторинг? Буду рад вашим историям и любимым приёмам, особенно если они помогли спасти прод в пятницу вечером.
Начинается всё с того, что я не верю своим догадкам. В большинстве баз, а я чаще всего работаю с PostgreSQL и MySQL, план достаётся парой команд: один вариант для прикидки и второй с фактическим выполнением, когда нужны реальные цифры. Важный момент: первый показывает предположение планировщика, а он ошибается куда чаще, чем нам кажется. Поэтому на реальных данных я почти всегда прошу показать фактические показатели, но делаю это только на реплике, чтобы не ловить блокировки и не тормозить прод.
Дальше самое ценное правило, которое меня переучило: план читают не сверху вниз, как текст, а от самых вложенных узлов к внешним. В том же PostgreSQL вложенность задаётся отступами, и сердце проблемы почти всегда живёт в глубине дерева. Я переключаю глаза в режим сканера: ищу узел с самой большой стоимостью и самым сильным расхождением между ожидаемым и фактическим числом строк. Уже на этом шаге пять минут приносят первый результат.
Ключевая метрика для меня — это пара «ожидалось и получилось». Когда планировщик думает, что вернётся десять строк, а приходит двести тысяч, он выбирает неправильный способ соединения или теряет индекс, и любой дальнейший анализ становится бессмысленным. Такое расхождение почти всегда означает устаревшую статистику, неудачное выражение в условии или приведение типа, из-за которого индекс перестаёт применяться. Просто обновив статистику, я не раз сокращал время запроса в десятки раз.
Отдельно смотрю на типы доступа к таблицам. Полное последовательное сканирование — вовсе не приговор: если таблица маленькая или выгружается большая часть строк, это самый дешёвый путь. Плохо другое: тяжёлое сканирование огромной таблицы там, где должно быть точечное чтение по индексу. Ещё обращаю внимание на повторные циклы у вложенного цикла, на промежуточную сортировку и на то, не переполняется ли память и не сбрасываются ли временные файлы на диск. Диск в плане — это почти всегда красный флаг.
Мой личный алгоритм выглядит так. Первая минута — беру медленный запрос и запускаю его с фактическими показателями на копии данных. Следующие несколько минут — читаю дерево снизу вверх и отмечаю самый дорогой узел. Дальше — сравниваю ожидаемые и фактические строки, проверяю статистику и условия в фильтрах. Последние минуты — формулирую гипотезу и проверяю её одним изменением: новый индекс, переписанное условие, убранное приведение типов. Правило простое: меняю по одному фактору за раз, иначе не пойму, что именно сработало.
Из инструментов мне хватает встроенных средств клиента: визуализатор плана в административной панели, вывод в текстовом или древовидном формате, расширение для автоматического логирования медленных планов и сбор статистики по запросам. Внешние сервисы выручают, когда дерево большое и хочется увидеть его целиком, но строить на них весь анализ не обязательно. Гораздо важнее привычка записывать: что было до и что стало после, с какими параметрами, на каком объёме данных.
Главный вывод, к которому я пришёл за несколько лет: план запроса — это не страшная простыня текста, а честный отчёт о том, что база решила сделать. Пятнадцать минут внимательного чтения обычно дешевле, чем неделя оптимизации наугад. Расскажите, а как вы ищете узкие места: читаете дерево плана вручную, доверяете визуализаторам или сразу смотрите в мониторинг? Буду рад вашим историям и любимым приёмам, особенно если они помогли спасти прод в пятницу вечером.