Web UI: список запросов, стадии и live-план
Когда запрос на Trino выполняется медленно, у вас есть три уровня инструментов диагностики. Самый глубокий — EXPLAIN ANALYZE VERBOSE и JMX-метрики. Самый поверхностный, но почти всегда первый — встроенный Web UI координатора. Он не требует ни настройки, ни сторонних систем: координатор отдаёт его на том же HTTP-порту, что и protocol API (по умолчанию 8080). Открываете http://coordinator:8080, и сразу видите, что происходит на кластере прямо сейчас.
Web UI — это окно в живую модель исполнения Trino. Всё, что вы изучали в предыдущих модулях — query, stages, tasks, splits, drivers, operators — здесь становится не абстракцией из документации, а кликабельным деревом с цифрами. Этот урок учит читать Web UI так, чтобы за 30 секунд понять, на какой стадии запрос «застрял» и почему.
Что показывает главная страница
Главная страница Web UI — это список запросов. Координатор хранит метаданные о завершённых запросах в памяти ограниченное время (по умолчанию информация о последних запросах доступна, пока не вытеснится новыми), поэтому UI — инструмент оперативной диагностики, а не исторического анализа. Для истории нужны event listeners, о которых речь в уроке 4.
В верхней части — сводка по кластеру: число активных воркеров, суммарная нагрузка (running queries, queued queries), пропускная способность в строках и байтах в секунду. Ниже — собственно список, каждая строка которого описывает один запрос.
Список можно фильтровать и сортировать. Самые полезные сортировки на практике — по CPU Time (находит запросы, которые жгут процессор кластера) и по Cumulative Memory (находит запросы-обжоры по памяти). Поле State позволяет мгновенно отделить QUEUED от RUNNING: если запрос долго висит в QUEUED, проблема не в плане и не в данных, а в resource group — кластер просто не дал запросу слот.
Перед тем как открывать страницу деталей и копаться в стадиях, посмотрите на State в списке. Очень частая ошибка диагностики — оптимизировать SQL запроса, который на самом деле просто стоит в очереди resource group. Эти два класса проблем лечатся совершенно по-разному.
Страница деталей запроса
Клик по Query ID открывает страницу деталей — главный диагностический экран Trino. Она разделена на несколько блоков.
Overview — сводка: текст SQL, пользователь, resource group, источник (CLI, JDBC, dbt), тайминги. Здесь же — Session-параметры, с которыми запрос был запущен: это критично, потому что неожиданное поведение часто объясняется случайно выставленным session property.
Resource Utilization и Timeline — графики потребления CPU и памяти во времени. По форме графика видно характер запроса: ровное плато CPU — равномерная нагрузка; пила — чередование стадий; резкий пик памяти в конце — финальная агрегация или сортировка собирает всё в одном месте.
Stages — дерево стадий распределённого плана. Это ядро страницы, разберём его отдельно ниже.
Tasks — таблица всех задач с разбивкой по воркерам. Здесь ищут перекос (skew): если одна задача обработала на порядок больше строк или потратила больше CPU, чем соседи по стадии, — это data skew, и именно он часто делает запрос медленным.
Внизу страницы — счётчики операторов и, отдельной кнопкой, JSON-представление запроса целиком. JSON — это полный дамп всего, что координатор знает о запросе; в нём лежат данные, которых нет в визуальном UI, и его удобно прикладывать к багрепортам.
Дерево стадий: где «застрял» запрос
Блок Stages показывает распределённый план как дерево. Напомним таксономию: stage — концептуальная фаза плана, она не исполняется сама; её исполняют tasks на воркерах. В Web UI каждая стадия — это карточка с агрегированными по её задачам числами.
Нумерация стадий идёт сверху вниз от корня: Stage 0 — корневая (финальный вывод клиенту), стадии с большими номерами — ближе к источникам данных. Данные текут снизу вверх: source-стадии читают из коннекторов, промежуточные обменивают данные через exchange, корневая отдаёт результат.
Каждая карточка стадии показывает: число задач, число splits (завершённых и всего), сколько строк прошло на вход и выход, потраченное CPU-время, объём данных в обменах (buffered/queued). Чтобы найти узкое место, смотрят на стадию, которая накопила больше всего CPU-времени или wall-time относительно других. Это и есть «горячая» стадия — оптимизировать имеет смысл именно её.
Полезные сигналы прямо в дереве:
- Перекос между задачами одной стадии. Карточка стадии показывает min/avg/max по задачам. Большой разрыв max от avg — data skew.
- Buffered data растёт, а CPU простаивает. Стадия-потребитель не успевает за продьюсером или, наоборот, заблокирована — backpressure через exchange-буферы.
- Source-стадия с огромным input rows и маленьким output rows. Читается много, а фильтр отсекает почти всё. Сигнал, что не сработал pushdown или dynamic filtering — данные читаются с диска зря.
| Что видно в дереве стадий | Вероятная причина | Куда копать дальше |
|---|---|---|
| Одна стадия копит почти всё CPU | Тяжёлая операция (join, aggregation) | EXPLAIN ANALYZE этой стадии, урок 2 |
| max строк на задачу намного больше avg | Data skew по ключу партиционирования | Ключ join/group by, урок 5 |
| Source-стадия читает все строки таблицы | Не сработал predicate pushdown / dynamic filtering | План запроса, модуль 8 |
| Стадия RUNNING, но CPU около нуля | Backpressure или ожидание splits | Exchange-буферы, scheduler |
Tasks: как находят перекос
Отдельно стоит блока Tasks — таблица всех задач запроса. Стадия в Trino распараллеливается на множество задач, каждая на своём воркере, и каждая обрабатывает свой набор сплитов. В идеале задачи одной стадии нагружены одинаково: стадия закончится, когда закончит последняя задача, поэтому равномерность — это и есть скорость стадии.
Перекос (data skew) ломает эту равномерность. Если значения ключа партиционирования распределены неравномерно — например, одно значение customer_id встречается в разы чаще остальных, или ключ содержит много NULL, — то задача, которой достался «тяжёлый» диапазон ключа, обработает кратно больше строк, чем соседи. Остальные задачи давно закончили и простаивают, а стадия всё ещё ждёт одну перегруженную.
В таблице Tasks это видно прямо: задачи отсортированы, и колонки строк, CPU-времени, памяти показывают разброс. Если у одной задачи эти числа на порядок выше, чем у медианной по той же стадии, — это перекос. Web UI не скажет, по какому ключу он возник, но точно укажет, что он есть и в какой стадии. Дальше причину ищут в SQL: какой ключ join или GROUP BY соответствует горячей стадии, нет ли в нём доминирующего значения или NULL.
Перекос коварен тем, что добавление воркеров его не лечит: новые воркеры получат лёгкие задачи и быстро освободятся, а одна тяжёлая задача как была, так и останется на одном воркере. Лечится перекос на уровне данных и запроса — пересмотром ключа, отдельной обработкой доминирующего значения, фильтрацией NULL, — а не масштабированием кластера. Поэтому распознать перекос по блоку Tasks важно: он отправляет диагностику по верному пути.
Live-план: исполнение в реальном времени
У страницы деталей запущенного запроса есть вкладка Live Plan. Она рисует план запроса как граф и в реальном времени подсвечивает прогресс: сколько строк уже прошло через каждый оператор, какие операторы активны, где данные «копятся». Это та же информация, что в дереве стадий, но визуализированная как граф операторов и обновляемая на лету.
Live-план особенно полезен для длинных запросов: вы запускаете тяжёлый аналитический запрос, открываете Live Plan и буквально наблюдаете, как данные текут по плану. Видно, на каком операторе фронт исполнения остановился. Если граф несколько секунд «стоит» на конкретном join — вы нашли узкое место, не дожидаясь конца запроса.
После завершения запроса для него доступен Stage Performance — статический срез того же графа с финальными числами. Live Plan отвечает на вопрос «что происходит прямо сейчас», Stage Performance — «что произошло».
Web UI отвечает на вопросы «где» и «сколько»: какая стадия горячая, сколько строк и CPU она съела, есть ли перекос. Но он не отвечает на вопрос «почему» план именно такой. За «почему» отвечает EXPLAIN — он показывает решения оптимизатора. Поэтому рабочая связка такая: Web UI локализует проблемную стадию, EXPLAIN ANALYZE объясняет её. Этому посвящён следующий урок.
Стоит помнить про ограничения. Web UI живёт на координаторе и хранит данные в его памяти ограниченное время — это инструмент «здесь и сейчас», не система мониторинга. В продакшене UI обычно закрывают аутентификацией: он показывает тексты SQL, имена пользователей, схему данных — чувствительную информацию. Доступ к UI стоит выдавать так же осторожно, как доступ к самому Trino.
Попробуй сам
Поднимите Trino локально (одного контейнера достаточно) и откройте Web UI на http://localhost:8080.
- Запустите заведомо тяжёлый запрос на встроенном коннекторе:
SELECT
l.returnflag,
count(*) AS cnt,
sum(l.extendedprice) AS revenue
FROM tpch.sf10.lineitem l
JOIN tpch.sf10.orders o ON l.orderkey = o.orderkey
WHERE o.orderdate >= DATE '1995-01-01'
GROUP BY l.returnflag
ORDER BY revenue DESC;
- Пока он выполняется, найдите его в списке запросов, откройте страницу деталей и переключитесь на Live Plan. Понаблюдайте, через какой оператор данные текут дольше всего.
- Откройте блок Stages. Найдите стадию, которая накопила больше всего CPU-времени. Это source-стадия скана
lineitemили стадия с join? - Откройте вкладку Tasks. Сравните min/avg/max строк по задачам внутри одной стадии — есть ли перекос?
- В правом верхнем углу страницы деталей найдите ссылку на JSON-представление запроса. Откройте его и найдите в нём поле с пиковой памятью запроса.
Цель — научиться за минуту локализовать «горячую» стадию, не открывая ни EXPLAIN, ни логи.