Сорок секунд: первый EXPLAIN ANALYZE

Чертёж без рейса: EXPLAIN

11 мин
Чему научишься
  • читать паспорт узла плана: cost=старт..итог, rows, width
  • понимать, почему cost — попугаи, а не миллисекунды
  • отличать «сколько строк узел прочитает» от «сколько отдаст наверх»
  • видеть, как дерево плана растёт: над сканом появляется новый этаж

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

Сегодня ты разглядываешь приборную доску штурмана и замечаешь странное: на ней нет ни одних часов. Шкалы, счётчики, градуировки — а секунд нигде нет.

КВЕРИ: Верно подмечено. Штурман не носит часов: когда он выбирает маршрут, рейс ещё не начался — мерить нечего. Он меряет работу: сколько страниц перелистать, сколько строк перещупать. Единицы у этой работы условные — своей линейки у штурмана нет, поэтому курсанты зовут их попугаями: как удава в старом мультфильме, которого мерили попугаями за отсутствием метра. Попугай не переводится ни в секунды, ни в байты; он годится ровно для одного — сравнить два маршрута между собой. Дальше в курсе слово «попугаи» означает только это: условные единицы cost.

Научимся читать эти шкалы. Открой любой план — и первое, что бросается в глаза, — это cost. С него и начнём.

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

Паспорт узла: три показания прибора

План — дерево узлов, и у каждого узла один и тот же формат паспорта:

Узел  (cost=старт..итог rows=прогноз width=байты)
  • cost=старт..итог — цена узла в условных единицах, и чисел здесь ДВА. Startup — сколько работы нужно, чтобы выдать первую строку. Total — чтобы выдать последнюю. Зачем два — увидишь через несколько минут, когда на чертеже появится узел, у которого эти цифры почти совпадают.
  • rows — сколько строк узел, по прогнозу, отдаст наверх. Прогноз, не факт: запрос не выполнялся.
  • width — средняя ширина одной такой строки в байтах. rows × width — сколько данных потечёт вверх по дереву.

Про cost запомни главное: это попугаи, а не миллисекунды. Единица — не время, а условная работа: назначает цену каждому действию — перелистнуть страницу, проверить строку — и складывает. Цена нужна ему ровно для одного: сравнивать маршруты МЕЖДУ СОБОЙ и брать самый дешёвый. Перевести попугаев в секунды нельзя: на другом железе та же цена обернётся другим временем.

Дерево читаешь сверху вниз, а исполняется оно снизу вверх — это ты помнишь ещё из Хранилища. Значит, нижние этажи заняты чтением таблиц, а верхние — работой над уже добытыми строками; что при этом показывают их приборы, увидишь на втором чертеже.

Открой паспорт самого простого маршрута — фильтра по журналу:

Чертёж из одного узла. Запрос не выполнялся: все цифры в скобках — прогноз. Найди оба числа cost, прогноз rows и width; строкой ниже — Filter, условие, которым скан отсеивает строки.

Читаем показания

cost=0.00..3763.00. Startup — ноль: последовательный проход отдаёт первую подходящую строку сразу, копить ему нечего. Total — 3763 попугая за весь журнал. Из чего сложилась эта цена, разберём через пару уроков, когда откроем карту ввода-вывода; пока это просто ценник маршрута.

Filter строкой ниже. Фильтр — не отдельный узел: скан читает все сто восемьдесят тысяч строк подряд и наружу пропускает только warn. Поэтому rows — это не «сколько прочитает», а «сколько отдаст наверх».

rows. На чертеже — три с лишним тысячи; запусти ячейку ещё раз — и цифра почти наверняка сдвинется: от прогона к прогону она гуляет. Так и должно быть: не пересчитывал warn-строки — он заглянул в статистику, выборочную перепись архива, а песочница пересобирает журнал и перепись на каждый запуск, каждый раз опрашивая чуть другие строки. На самом деле warn-показаний в журнале ровно 3600: прогноз промахивается на пару сотен строк — такой точности для выбора маршрута более чем достаточно.

width=21. Средняя строка результата — 21 байт: ровно те четыре колонки, которые ты попросил. Попроси SELECT * — width вырастет, и вверх по дереву потечёт больше байтов.

КВЕРИ: Прогноз rows — самое важное показание на всей доске. Пока он честный, штурман выбирает хорошие маршруты. Когда врёт — начинаются сорок секунд. Запомни эту пару — прогноз и факт: мы к ней ещё вернёмся.

Теперь заставь штурмана достать второй механизм: попроси тот же список, но по порядку значений.

Тот же фильтр плюс ORDER BY value — и дерево стало двухэтажным: над сканом вырос узел Sort. Сравни две пары cost: у скана startup 0.00, у Sort первая цифра почти равна второй.

Дерево растёт

ORDER BY не изменил маршрут скана: та же цена 0.00..3763.00, тот же width=21. Прогноз rows снова чуть другой — но это всё та же гуляющая перепись из прошлой ячейки, а не эффект ORDER BY. Зато над сканом вырос новый этаж: Sort, со стрелкой -> перед сканом-ребёнком. Сверху написано «отсортируй то, что отдаст скан»; исполнение, как всегда, начинается снизу: скан добывает warn-строки, Sort их выстраивает.

Теперь посмотри на пару cost у Sort — в ней вся суть двух чисел. Первая цифра почти равна второй: без малого четыре тысячи попугаев ещё ДО первой выданной строки. Сортировка не может отдать наверх даже одну строку, пока не проглотит все: вдруг последняя строка скана окажется наименьшей. Скан — узел-ручей, строки текут сразу; Sort — узел-плотина, он копит. Пара старт..итог и говорит тебе, ручей перед тобой или плотина.

И ещё одна привычка чтения: цена узла включает цену его детей. Из четырёх тысяч попугаев Sort бóльшую часть — 3763 — унаследовал от скана; собственная работа сортировки стоит немного: три с половиной тысячи строк — не тот объём, на котором она потеет.

Чертёж ты теперь читаешь. Но помни: машина пока не прочитала по-настоящему ни одной страницы журнала — всё это прогнозы штурмана. В следующем уроке отправим запрос в настоящий рейс — EXPLAIN ANALYZE — и на приборной доске впервые появится секундомер: оценка встретится с фактом.

Проверь себя
В плане стоит строка Seq Scan on ls_readings (cost=0.00..3763.00 rows=3540 width=21). Что означает вторая цифра пары cost — 3763.00?
Проверь себя
Курсант из соседней смены смотрит на cost=0.00..3763.00 и объявляет: «3763 — значит, запрос идёт примерно 3,7 секунды». Прав ли он?
Закрепление: реши задачи
Решено 0 из 1