Learning Platform
Глоссарий Troubleshooting
Урок 09.05 · 23 мин
Средний
cbopushdownpredicate-pushdownspi

Pushdown: predicate, projection, aggregation

Самая дешёвая работа — та, которую не пришлось делать. Если вы джойните таблицу из PostgreSQL и вам нужны только три столбца из тридцати и только строки за последний день, бессмысленно тянуть из PostgreSQL всю таблицу целиком, а потом отбрасывать 90% столбцов и 99% строк уже в Trino. Лучше попросить PostgreSQL отдать сразу только нужное.

Это и есть pushdown — проталкивание части работы из Trino вниз, в источник данных. Pushdown — один из главных рычагов производительности Trino, особенно в федеративных запросах. Этот урок — про три вида pushdown (predicate, projection, aggregation), про то, как они работают через SPI, и почему pushdown зависит от коннектора.


Идея pushdown: считать ближе к данным

Без pushdown модель работы Trino простая: коннектор отдаёт сырые данные таблицы, Trino применяет к ним фильтры, проекции, агрегации своими операторами на воркерах. Корректно, но расточительно: по сети из источника тащится всё, включая то, что тут же будет отброшено.

Pushdown переворачивает это там, где возможно: часть операций Trino передаёт источнику, и источник применяет их сам, до того как данные уйдут в Trino. Выигрыш двойной. Меньше данных едет по сети из источника в Trino. И часть вычислений делает сам источник — а он часто к этой работе приспособлен лучше: у реляционной СУБД есть индексы, у Parquet-файла — статистика по row group’ам.

Почему pushdown особенно важен для федерации. Когда источник — внешняя СУБД, канал между ней и Trino — это сеть, и она легко становится бутылочным горлышком. Протолкнуть в СУБД фильтр, отсекающий 99% строк, означает прогнать по сети в 100 раз меньше данных. Для федеративных запросов pushdown — часто разница между «секунды» и «минуты».

Без pushdown и с pushdown
Без pushdownИсточник отдаёт все строки и столбцы, Trino фильтрует и проецирует сам. По сети едет всё, включая отбрасываемое
pushdown переносит работу вниз
С pushdownTrino передаёт фильтр и проекцию источнику, источник применяет их сам. По сети едет только нужное

Predicate pushdown: проталкивание фильтра

Predicate pushdown — проталкивание условия WHERE в источник. Вместо «отдай все строки, Trino сам отфильтрует» Trino говорит источнику: «отдай только строки, удовлетворяющие этому условию».

SELECT name, email
FROM postgresql.public.users
WHERE country = 'DE' AND created_at >= DATE '2026-01-01';

С predicate pushdown условие country = 'DE' AND created_at >= ... уходит в PostgreSQL — он применит его сам, возможно используя индекс по country, и вернёт Trino уже отфильтрованный набор. Без pushdown PostgreSQL отдал бы всю таблицу users, а Trino выбросил бы всех не-немцев уже у себя, впустую прогнав их по сети.

Predicate pushdown работает не только в РСУБД. В файловых форматах он включает отсечение на уровне метаданных: для Parquet и ORC коннектор по статистике row group’ов (min/max столбцов) пропускает целые блоки, которые заведомо не содержат строк под условие. А для партиционированных таблиц predicate pushdown по партиционирующему столбцу даёт partition pruning — целые партиции не читаются вовсе.

Parquet: Metadata и Statistics ClickHouse: PREWHERE — фильтрация до чтения

Projection и dereference pushdown: проталкивание проекции

Projection pushdown — проталкивание выбора столбцов. Trino сообщает источнику, какие именно столбцы нужны, чтобы тот не отдавал остальные.

SELECT user_id, email
FROM postgresql.public.users;

Из тридцати столбцов таблицы users запросу нужны два. С projection pushdown Trino просит у PostgreSQL только user_id и email — остальные 28 столбцов по сети не едут. В колоночных форматах (Parquet, ORC) projection pushdown особенно силён: колоночное хранение позволяет физически прочитать с диска только нужные столбцы, не касаясь остальных, — это и есть главное преимущество колоночных форматов, и pushdown даёт Trino им воспользоваться.

Частный, более тонкий случай — dereference pushdown. Он касается вложенных структур ROW. Если столбец — структура с десятью полями, а запросу нужно одно вложенное поле, dereference pushdown проталкивает в источник обращение именно к этому полю:

SELECT event.user.country
FROM iceberg.analytics.events;

Здесь event — вложенная структура ROW. Dereference pushdown означает: коннектор прочитает из Parquet только путь event.user.country, не материализуя весь объект event. Мы упоминали эту оптимизацию во втором модуле, когда говорили про типизированный ROW — вот где она работает. Условие dereference pushdown — статически известная схема ROW: движок знает раскладку полей и может попросить ровно нужное.

Projection и dereference pushdown
Таблица: 30 столбцов, один — ROWШирокая таблица, среди столбцов — вложенная структура ROW со многими полями
запросу нужно 2 столбца и 1 вложенное поле
Projection + dereferenceTrino сообщает источнику точный список нужных столбцов и путь к нужному вложенному полю ROW
pushdown
Источник читает только нужноеКолоночный формат физически читает только указанные столбцы и поля, остальное не касается диска и сети

Aggregation pushdown: проталкивание агрегации

Aggregation pushdown — самый продвинутый вид. Он проталкивает в источник саму агрегацию: count, sum, avg, GROUP BY.

SELECT country, count(*)
FROM postgresql.public.users
GROUP BY country;

Без aggregation pushdown Trino вытянул бы из PostgreSQL все строки users и сгруппировал их своими операторами. С aggregation pushdown в PostgreSQL уходит сам SELECT country, count(*) ... GROUP BY country — PostgreSQL выполняет группировку и возвращает Trino уже готовый компактный результат: по строке на страну. Если в таблице сто миллионов пользователей и двести стран, по сети едут двести строк вместо ста миллионов — выигрыш на порядки.

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


Pushdown работает через SPI и зависит от коннектора

Теперь принципиальное — почему pushdown не «всегда есть». Pushdown — это диалог между движком и коннектором, и ведётся он через Connector SPI.

Механика такая. Оптимизатор Trino, дойдя до операции, которую в принципе можно протолкнуть, обращается к коннектору через специальные методы SPI — концептуально это applyFilter, applyProjection, applyAggregation. Коннектор отвечает: «эту операцию я приму на себя» — целиком, частично или «не приму, считай сам». Если коннектор принял — операция уходит в источник; если нет — её выполняют операторы Trino, как обычно.

Отсюда два следствия. Первое: pushdown — свойство коннектора, а не движка. Один коннектор реализует все три вида pushdown, другой — только predicate, третий — никакого. Объём поддержки нужно знать для конкретного коннектора. Второе: pushdown может быть частичным. Коннектор вправе принять половину сложного WHERE (то, что умеет протолкнуть) и вернуть остальное Trino — тогда часть условия проверит источник, часть доделает Trino.

Pushdown — диалог через SPI
Оптимизатор TrinoДойдя до операции, которую можно протолкнуть, спрашивает коннектор через applyFilter/applyProjection/applyAggregation
applyFilter / applyAggregation
КоннекторОтвечает: приму целиком, приму частично или не приму. Зависит от возможностей коннектора и источника
принято -> в источник
Источник или операторы TrinoПринятое уходит в источник; отвергнутое выполняют операторы Trino на воркерах

Как увидеть pushdown в плане

Сработал pushdown или нет — прямо видно в EXPLAIN. Главный признак: исчезновение отдельных операторов из плана. Когда фильтр протолкнут, в плане нет отдельного оператора Filter — условие «вшито» в узел TableScan источника. Когда фильтр не протолкнут — TableScan отдаёт сырые строки, а над ним стоит отдельный Filter.

EXPLAIN
SELECT name FROM postgresql.public.users WHERE country = 'DE';

Если predicate pushdown сработал, в плане у TableScan будет указано само условие (в выводе видна конструкция вроде constraint или подзапрос с уже включённым WHERE), а отдельного Filter-узла не будет. Если не сработал — увидите TableScan без условия плюс отдельный Filter[country = 'DE'] над ним. То же для агрегации: pushdown сработал — Aggregate-узел исчезает из плана Trino, агрегация ушла в источник; не сработал — Aggregate стоит в плане. Чтение EXPLAIN именно с этой стороны — «какие операторы пропали» — основной способ проверить, что pushdown работает.

TIP

Привыкайте читать EXPLAIN федеративных запросов через вопрос «что исчезло из плана». Если WHERE по внешней СУБД дал в плане отдельный Filter, а не вшился в TableScan, — predicate pushdown не сработал, и по сети, скорее всего, едут лишние строки. Если GROUP BY оставил в плане Aggregate-узел над сканом внешнего источника — агрегация считается в Trino, а не в источнике. Это сигнал: либо коннектор не поддерживает нужный вид pushdown, либо условие записано так, что протолкнуть его нельзя. Pushdown — то, что стоит проверять в первую очередь, когда федеративный запрос медленный.


Попробуй сам

На песочнице курса (Trino 481):

  1. Возьмите EXPLAIN SELECT orderkey FROM tpch.sf1.orders WHERE orderstatus = 'F';. Найдите узел TableScan/ScanFilter и определите, вшито ли условие orderstatus = 'F' в скан или стоит отдельным Filter-узлом. Сформулируйте, сработал ли predicate pushdown.

  2. Сравните два плана: EXPLAIN SELECT * FROM tpch.sf1.orders; и EXPLAIN SELECT orderkey, custkey FROM tpch.sf1.orders;. Посмотрите, как в плане отражается, что во втором случае запрашиваются только два столбца. Объясните, какой вид pushdown тут работает и почему для колоночных данных он особенно ценен.

  3. Рассуждение в двух абзацах. Первый: почему aggregation pushdown в федеративном запросе SELECT country, count(*) FROM external_db.users GROUP BY country может ускорить запрос на порядки — распишите, сколько строк едет по сети с pushdown и без. Второй: почему наличие этого ускорения не гарантировано и от чего оно зависит.


Проверка знанийKnowledge check
Что такое pushdown, чем различаются predicate, projection и aggregation pushdown, и почему pushdown зависит от коннектора?
ОтветAnswer
Pushdown — это проталкивание части работы из Trino вниз, в источник данных: вместо того чтобы тянуть из источника все сырые данные и обрабатывать их операторами Trino на воркерах, движок передаёт часть операций источнику, и тот применяет их сам до отправки данных. Выигрыш двойной — меньше данных едет по сети из источника в Trino, и часть вычислений делает сам источник, который часто к этому приспособлен лучше (индексы у РСУБД, статистика row group'ов у Parquet). Для федеративных запросов это особенно важно, потому что канал до внешней СУБД легко становится бутылочным горлышком. Три вида различаются тем, что проталкивается. Predicate pushdown проталкивает условие WHERE: источник возвращает только строки, удовлетворяющие условию, — в файловых форматах это даёт отсечение row group'ов по min/max и partition pruning. Projection pushdown проталкивает выбор столбцов: источник отдаёт только нужные столбцы, а не все, — в колоночных форматах это позволяет физически прочитать с диска лишь нужные столбцы; его тонкий случай, dereference pushdown, проталкивает обращение к конкретному вложенному полю ROW. Aggregation pushdown — самый продвинутый — проталкивает саму агрегацию (count, sum, GROUP BY): источник выполняет группировку и возвращает компактный результат, и если в таблице сто миллионов строк и двести групп, по сети едут двести строк вместо ста миллионов. Pushdown зависит от коннектора, потому что это диалог движка и коннектора через Connector SPI: оптимизатор предлагает коннектору операцию методами вроде applyFilter, applyProjection, applyAggregation, а коннектор отвечает, примет ли он её — целиком, частично или совсем нет. Поэтому pushdown — свойство коннектора, а не движка: один коннектор реализует все три вида, другой только predicate, третий никакого; и pushdown может быть частичным — коннектор вправе принять часть сложного условия и вернуть остальное Trino.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что такое pushdown в Trino?

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

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

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

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