Learning Platform
Глоссарий Troubleshooting
Урок 14.04 · 22 мин
Средний
fault-tolerancetask-sizingadaptive-planninginternals

Task sizing и adaptive planning при FTE

В режиме FTE с retry-policy=TASK промежуточные данные не растворяются в потоке, а материализуются через exchange manager. У этого есть неожиданный побочный эффект: материализованный промежуток можно измерить. А зная фактический размер данных, FTE делает то, чего классический pipelined-Trino не может, — подгоняет размер задач под реальность и переоптимизирует план уже в процессе исполнения.

Этот урок — про две способности, которые FTE получает «бесплатно» из материализации промежутка: task sizing (подбор размера задач) и adaptive planning (адаптивное перепланирование).


Откуда у FTE точные числа

Сначала — ключевое наблюдение, без которого ни task sizing, ни adaptive planning не имеют смысла.

В классическом pipelined-Trino (retry-policy=NONE) данные текут конвейером и нигде не задерживаются. Поэтому в момент планирования у Trino нет фактических размеров промежутка — есть лишь оценки cost-based optimizer по статистике таблиц. А оценки ошибаются: реальная кардинальность join, реальный объём после фильтра могут отличаться от прогноза в разы.

В FTE с retry-policy=TASK всё иначе. Стадия-продьюсер сначала полностью материализует свой вывод в спул через exchange manager, и только потом стадия-консьюмер начинает читать. К моменту старта консьюмера exchange manager точно знает объём промежутка — он лежит в хранилище, его размер измерен, а не угадан.

Pipelined: оценки vs FTE: измеренный факт
Pipelined (NONE): только оценки CBOДанные текут конвейером, нигде не материализуясь. Размер промежутка в момент планирования — лишь прогноз по статистике таблиц, который может сильно ошибаться
FTE материализует промежуток
FTE (TASK): измеренный фактический размерСтадия-продьюсер полностью спулит вывод, прежде чем консьюмер начнёт читать. Объём промежутка измерен по содержимому хранилища — это факт, а не прогноз

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


Task sizing: подбор размера задач

Task sizing — подбор количества и размера задач под фактический объём данных стадии.

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

FTE с retry-policy=TASK решает это так. Зная измеренный объём промежутка, движок назначает столько задач, сколько нужно под этот объём, и каждой задаче даёт примерно целевую порцию работы. Мало данных — мало задач без лишнего overhead. Много данных — больше задач, каждая разумного размера.

Поведением task sizing управляют свойства FTE. Ориентировочные значения по умолчанию:

СвойствоДефолтСмысл
fault-tolerant-execution-standard-split-size64MBЦелевой объём данных на один split при FTE
fault-tolerant-execution-max-task-split-count2048Максимум splits на одну задачу
fault-tolerant-execution-task-memory5GBОриентир памяти на задачу при FTE
# etc/config.properties — ориентиры task sizing при FTE
fault-tolerant-execution-standard-split-size=64MB
fault-tolerant-execution-max-task-split-count=2048
fault-tolerant-execution-task-memory=5GB
NOTE

Точные имена и дефолты свойств task sizing меняются между релизами Trino — сверяй их со страницей admin/fault-tolerant-execution для версии 481, а не со старыми гайдами. Идея же стабильна: FTE дробит работу на задачи разумного размера, опираясь на измеренный объём, а не на фиксированное число.

Почему разумный размер задачи так важен именно в FTE? Из-за retry-policy=TASK. Задача — это единица переисполнения при сбое. Слишком большая задача — дорогой ретрай: упала почти доделанная огромная задача, и весь её объём придётся пересчитывать. Слишком мелкая — overhead планирования и спулинга съедает пользу. Task sizing держит задачи в «золотой середине»: достаточно мелкие, чтобы ретрай был дёшев, достаточно крупные, чтобы накладные расходы не доминировали.

Task sizing: число задач под измеренный объём
Промежуток измерен: мало данныхExchange manager знает: данных стадии немного. Назначать сотню задач — пустой overhead
та же стадия, другой объём
Промежуток измерен: много данныхДанных стадии много. Назначается больше задач, каждой — целевая порция работы разумного размера, дешёвая для ретрая

Adaptive planning: перепланирование в рантайме

Вторая способность — adaptive planning, адаптивное перепланирование. Если task sizing подгоняет размер задач, то adaptive planning меняет сам план запроса уже в процессе исполнения.

Классический Trino строит распределённый план один раз, до старта, на оценках CBO. Дальше план зафиксирован. Если CBO ошибся в кардинальности — план остаётся плохим до конца запроса.

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

Самый показательный пример — выбор типа join. Из модуля про CBO: broadcast join (build-сторона целиком копируется на каждую ноду) выгоден, когда build-сторона маленькая; partitioned join (обе таблицы перераспределяются по хэшу) нужен, когда она большая. CBO выбирает тип join по оценке размера build-стороны. Если оценка промахнулась — план застрял с неверным типом.

AdaptivePlanner это чинит. Build-сторона материализована — её фактический размер известен. Оказалось, что CBO ждал маленькую build-сторону и выбрал broadcast, а реально она огромная? AdaptivePlanner на лету переключит ещё не исполненный join на partitioned, пока неверный broadcast не положил воркеры по памяти.

Spark AQE: адаптивный query execution
Adaptive planning: коррекция плана по факту
План по оценкам CBOДо старта CBO по статистике таблиц выбрал, например, broadcast join, ожидая маленькую build-сторону
стадия материализована, факт измерен
AdaptivePlanner: факт разошёлся с прогнозомПосле материализации видно: build-сторона реально огромная. Прогноз CBO промахнулся
переоптимизация остатка плана
Остаток плана исправленAdaptivePlanner переключает ещё не исполненный join на partitioned — по реальным числам, пока ошибка не уронила запрос

Почему adaptive planning доступен именно в FTE

Логичный вопрос: почему AdaptivePlanner работает при FTE, а не в классическом режиме? Ведь идея «исправлять план по факту» полезна всегда.

Ответ — снова в материализации. Чтобы переоптимизировать план по фактическим числам, эти числа нужно иметь. В pipelined-режиме промежуток не материализуется — он течёт мимо, его никто целиком не видит и не измеряет. Фактического размера вывода стадии в классическом Trino попросту не существует как наблюдаемой величины до самого конца запроса. Перепланировать не на чем.

FTE с retry-policy=TASK материализует промежуток ради отказоустойчивости — и этот же материализованный промежуток дарит точные числа. Adaptive planning — это, по сути, бонус: возможность, которая появляется как побочный продукт того, что промежуток и так уже спулен и измерен.

TIP

Запомни связку: материализация промежутка в FTE служит сразу трём целям. Первая — отказоустойчивость (упавшую задачу есть откуда переисполнить). Вторая — task sizing (число задач под измеренный объём). Третья — adaptive planning (коррекция плана по фактическим числам). Один архитектурный приём — спулинг промежутка через exchange manager — оплачивает все три способности разом.


Цена и где это уместно

Task sizing и adaptive planning — не бесплатны. Они существуют поверх материализации промежутка, а материализация добавляет латентность: стадия-консьюмер ждёт, пока продьюсер полностью спулится, вместо того чтобы начать на первых же строках. Для коротких интерактивных запросов это плохой размен — там и retry-policy=NONE достаточно.

Зато для долгих batch-запросов выигрыш существенный. Именно у тяжёлых многостадийных запросов оценки CBO ошибаются чаще всего (ошибка кардинальности накапливается через стадии), и именно там цена плохого плана максимальна. FTE с task sizing и adaptive planning превращает «один раз ошиблись в плане — мучаемся весь запрос» в «ошиблись в прогнозе — исправились по факту на следующей стадии».


Попробуй сам

Понаблюдай адаптивность FTE на тестовом Trino с настроенным exchange manager.

  1. Используй кластер из прошлого урока — с exchange manager на S3/MinIO и retry-policy=TASK.
  2. Возьми многостадийный запрос с join таблиц, по которым нет собранной статистики (создай таблицы и не запускай ANALYZE) — так оценки CBO заведомо неточны. Запусти его и сними EXPLAIN ANALYZE.
  3. Запусти тот же запрос с retry-policy=NONE и с retry-policy=TASK. В Web UI и в выводе EXPLAIN ANALYZE сравни число задач по стадиям и типы join. Обрати внимание, отличается ли распределение работы между задачами.
  4. Поразмышляй: почему именно отсутствие статистики делает выигрыш adaptive planning особенно заметным, и почему для короткого запроса по tpch.tiny разница была бы несущественной.

Цель — увидеть, что FTE не просто «ретраит задачи», а ещё и умнее распределяет работу и правит план по фактическим числам.


Проверка знанийKnowledge check
Почему task sizing и adaptive planning доступны именно в режиме FTE с retry-policy=TASK, а не в классическом pipelined-Trino? Какая общая причина стоит за обеими способностями?
ОтветAnswer
Общая причина — материализация промежуточных данных обмена, которую FTE с retry-policy=TASK выполняет ради отказоустойчивости. В классическом pipelined-Trino данные текут конвейером и нигде не задерживаются: промежуток никто целиком не видит и не измеряет, поэтому в момент планирования у движка есть только оценки cost-based optimizer по статистике таблиц — догадки, которые могут ошибаться в разы. Фактический размер вывода стадии в pipelined-режиме просто не существует как наблюдаемая величина до конца запроса. В FTE с retry-policy=TASK стадия-продьюсер полностью спулит свой вывод через exchange manager, прежде чем консьюмер начнёт читать, — и к этому моменту объём промежутка точно измерен по содержимому хранилища. Task sizing использует это измерение, чтобы назначить столько задач, сколько нужно под фактический объём, давая каждой целевую порцию работы: мало данных — мало задач без overhead, много данных — больше задач разумного размера, дешёвых для ретрая. Adaptive planning через компонент AdaptivePlanner использует то же измерение, чтобы после завершения стадии сравнить фактический объём её вывода с прогнозом и переоптимизировать ещё не исполненную часть плана — например, переключить join с broadcast на partitioned, если build-сторона оказалась огромной вопреки оценке CBO. Обе способности невозможны без фактических чисел, а фактические числа появляются только потому, что промежуток материализован. Adaptive planning и task sizing — по сути бонус: побочный продукт спулинга, который FTE и так делает ради отказоустойчивости.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Откуда FTE с retry-policy=TASK берёт точные размеры промежуточных данных, которых нет у классического pipelined-Trino?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5