Learning Platform
Глоссарий Troubleshooting
Урок 12.03 · 24 мин
Средний
federationpushdownpredicate-pushdownaggregation-pushdown

Pushdown в источник: что Trino отдаёт базе, а что считает сам

В прошлом уроке мы видели, что одна из задач JDBC-коннектора — транслировать часть запроса Trino в SQL источника. Эта «часть» — и есть ключ к производительности всей федерации. Когда Trino обращается к источнику, у него есть фундаментальный выбор: вычитать из источника сырые данные и обработать их у себя — или попросить источник самому выполнить часть работы и отдать уже готовый результат. Этот выбор называется pushdown, и от него зависит, будет ли федеративный запрос быстрым или невыносимо медленным. Этот урок разбирает механику pushdown, его виды и то, как увидеть, сработал ли он.

Два способа выполнить запрос к источнику

Возьмём конкретный запрос к таблице PostgreSQL:

SELECT count(*)
FROM postgresql.public.orders
WHERE status = 'completed';

Таблица orders в PostgreSQL — 50 миллионов строк, из них status = 'completed' — 3 миллиона. Есть два принципиально разных способа это выполнить.

Способ первый, без pushdown. Trino говорит источнику: «отдай мне всю таблицу orders». Источник отдаёт все 50 миллионов строк. Они едут по сети в Trino. Уже в Trino фильтр WHERE status = 'completed' отсекает строки, и count(*) считает результат. Итог верный — но по сети проехали 50 миллионов строк, чтобы получить одно число. Сеть и память Trino потрачены впустую на 47 миллионов строк, которые сразу же отброшены.

Способ второй, с pushdown. Trino понимает, что фильтрацию и подсчёт умеет сделать сам PostgreSQL. Он формирует и отправляет источнику SQL вроде SELECT count(*) FROM orders WHERE status = 'completed'. PostgreSQL исполняет это своим движком — у него для этого есть индексы и оптимизатор — и возвращает Trino одно число. По сети проехало одно значение вместо 50 миллионов строк.

Один запрос, два способа: с pushdown и без
Без pushdownTrino вычитывает из источника всю таблицу, фильтрует и агрегирует у себя; по сети едут все 50 млн строк ради одного числа
pushdown меняет картину
С pushdownTrino отправляет источнику SQL с фильтром и count; PostgreSQL считает сам и возвращает одно число

Это и есть pushdown — проталкивание части запроса в источник. Разница между двумя способами на больших таблицах — это разница между секундами и десятками минут, между нормальным запросом и запросом, кладущим кластер. Pushdown — самый важный фактор производительности федерации.

Виды pushdown

Trino умеет проталкивать в источник не только фильтр. Основные виды pushdown — те же, что упоминались в модуле про cost-based optimizer, здесь рассмотрим их в контексте федерации.

Predicate pushdown — проталкивание фильтра. Условие WHERE отправляется источнику, и тот возвращает только подходящие строки. Источник применяет фильтр своими средствами, в том числе используя свои индексы. Это базовый и самый частый вид pushdown.

Projection pushdown — проталкивание выбора колонок. Если запросу нужны две колонки из тридцати, Trino просит источник вернуть только эти две (SELECT col1, col2 вместо SELECT *). Для колоночных источников это сокращает чтение; для строчных — хотя бы сокращает объём передачи по сети.

Aggregation pushdown — проталкивание агрегации. Функции count, sum, avg, GROUP BY Trino может отдать источнику, и тот вернёт уже агрегированный, маленький результат вместо сырых строк. Это самый эффектный вид pushdown: агрегация схлопывает миллионы строк в десятки.

Limit / TopN pushdown — проталкивание LIMIT и ORDER BY ... LIMIT. Если нужны 10 строк, нет смысла тянуть всю таблицу — LIMIT 10 уходит источнику.

Вид pushdownЧто проталкиваетсяЧто экономит
PredicateWHERE-условиеПередачу отфильтрованных строк по сети
ProjectionСписок нужных колонокЧтение и передачу ненужных колонок
Aggregationcount, sum, GROUP BYПередачу сырых строк — источник отдаёт агрегат
Limit / TopNLIMIT, ORDER BY + LIMITПередачу строк сверх лимита
ClickHouse: PREWHERE как встроенный pushdown фильтрации

Почему pushdown срабатывает не всегда

Ключевой нюанс: pushdown — не гарантия, а возможность. Сработает он или нет, зависит от двух условий.

Первое — поддержка коннектором. Каждый JDBC-коннектор реализует pushdown в своём объёме. Один поддерживает aggregation pushdown полно, другой — частично, третий — только predicate. Reference-коннекторы (PostgreSQL, MySQL, SQL Server, Oracle) поддерживают pushdown наиболее полно — это одна из причин их статуса эталонных; у менее ходовых коннекторов набор уже.

Второе — транслируемость конкретной операции. Чтобы протолкнуть операцию, Trino должен суметь выразить её в SQL источника так, чтобы семантика совпала. Простой фильтр status = 'completed' транслируется почти всюду. Но если в условии — функция Trino, которой в источнике нет точного аналога, или поведение функции в источнике отличается (другие правила сравнения строк, другая работа с NULL), Trino не станет проталкивать операцию: некорректный результат хуже медленного. В таком случае Trino честно вычитает данные и обработает их сам, чтобы гарантировать правильность.

Отсюда практическое следствие: то, как написан запрос, влияет на pushdown. Фильтр по «чистому» сравнению колонки с константой протолкнётся охотнее, чем фильтр, обёрнутый в нестандартное выражение. Понимание pushdown — это понимание того, почему один вариант запроса быстрый, а логически эквивалентный — медленный.

WARNING

Pushdown не срабатывает автоматически для всего. Если федеративный запрос неожиданно медленный, типичная причина — операция, которую Trino не смог протолкнуть в источник, и он вынужден тянуть сырые данные. Это не поломка: Trino выбирает корректность, когда не уверен в эквивалентности трансляции. Задача инженера — увидеть, что не протолкнулось, и по возможности переписать запрос так, чтобы pushdown стал возможен.

SQL: читаем EXPLAIN и понимаем план выполнения

Как увидеть, сработал ли pushdown

Догадки тут не нужны — есть EXPLAIN. План запроса показывает, что Trino проталкивает в источник, а что выполняет сам.

Главный признак — как выглядит обращение к источнику в плане. Когда pushdown сработал, в плане у узла обращения к источнику видно, что в источник ушёл SQL уже с фильтром, с нужными колонками, с агрегацией. Когда pushdown не сработал, в плане видна отдельная операция Trino — отдельный узел Filter, отдельный узел Aggregate — выполняемая уже после чтения сырых данных из источника.

-- Смотрим план: протолкнулись ли фильтр и агрегация в PostgreSQL
EXPLAIN
SELECT status, count(*)
FROM postgresql.public.orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY status;

Читая план, задавайте себе вопрос: где выполняется фильтр и агрегация — внутри обращения к источнику или отдельным шагом в Trino? Если внутри обращения к источнику — pushdown сработал, источник вернёт маленький агрегат. Если отдельным шагом Trino — pushdown не сработал, и Trino тянет сырые строки. Тот же план в EXPLAIN ANALYZE покажет фактический объём данных, прочитанный из источника, — прямое подтверждение, прокачаны ли через сеть миллионы строк или единицы.

Чтение EXPLAIN: где выполняется работа
Pushdown сработалВ плане обращение к источнику уже содержит фильтр и агрегацию — источник возвращает готовый маленький результат
сравните с
Pushdown не сработалВ плане отдельные узлы Filter и Aggregate после чтения источника — Trino тянет сырые строки и обрабатывает сам

Pushdown и баланс нагрузки

Последняя мысль, важная для эксплуатации. Pushdown переносит работу с Trino на источник — и это палка о двух концах.

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

Обычно это всё равно меньшее зло, чем вытягивание миллионов строк (которое нагружает и сеть, и источник на полное сканирование, и Trino). Но в проде это учитывают: тяжёлые федеративные запросы направляют на read-реплику источника, а не на боевую базу. Pushdown — мощный инструмент, и как любой мощный инструмент требует понимания, куда именно он переносит нагрузку.

Попробуй сам

В песочнице с подключённым каталогом PostgreSQL создайте в базе таблицу orders (хотя бы несколько десятков тысяч строк, колонки status, order_date, amount). Упражнение первое: выполните EXPLAIN для SELECT count(*) FROM postgresql.public.orders WHERE status = 'completed' и найдите в плане, ушёл ли фильтр и подсчёт в источник или выполняются отдельными шагами в Trino. Упражнение второе: выполните EXPLAIN для запроса с GROUP BY и агрегацией sum(amount) — протолкнулась ли агрегация. Затем сравните с EXPLAIN ANALYZE — сколько строк фактически прочитано из источника. Упражнение третье, на границу pushdown: оберните колонку в фильтре в какое-нибудь нестандартное выражение или функцию и посмотрите EXPLAIN снова — изменилось ли поведение pushdown. Письменно объясните: почему pushdown — самый важный фактор производительности федерации, почему он срабатывает не всегда, и на какой компромисс по нагрузке инженер идёт, проталкивая тяжёлую агрегацию в боевую транзакционную базу.


Проверка знанийKnowledge check
Что такое pushdown, какие у него бывают виды, почему он срабатывает не всегда и как увидеть, сработал ли он?
ОтветAnswer
Pushdown — это проталкивание части запроса в источник: вместо того чтобы вычитать из источника сырые данные и обработать их у себя, Trino просит источник самому выполнить часть работы и отдать готовый результат. Разница огромна: запрос count со строгим фильтром к таблице на 50 миллионов строк без pushdown гонит по сети все 50 миллионов строк ради одного числа, а с pushdown отправляет источнику SQL с фильтром и count, и источник возвращает одно число. На больших таблицах это разница между секундами и десятками минут — pushdown самый важный фактор производительности федерации. Основные виды: predicate pushdown проталкивает WHERE-условие, и источник возвращает только подходящие строки, используя свои индексы; projection pushdown проталкивает список нужных колонок вместо SELECT *; aggregation pushdown проталкивает count, sum, GROUP BY, и источник отдаёт маленький агрегат вместо сырых строк; limit и TopN pushdown проталкивают LIMIT и ORDER BY с LIMIT. Pushdown срабатывает не всегда — это возможность, а не гарантия, и зависит от двух условий. Первое — поддержка коннектором: каждый JDBC-коннектор реализует pushdown в своём объёме, reference-коннекторы поддерживают его наиболее полно, у менее ходовых набор уже. Второе — транслируемость конкретной операции: чтобы протолкнуть операцию, Trino должен выразить её в SQL источника так, чтобы семантика совпала; если в условии функция без точного аналога в источнике или с другим поведением, Trino не станет проталкивать — некорректный результат хуже медленного — и честно обработает данные сам. Отсюда то, как написан запрос, влияет на pushdown. Увидеть, сработал ли pushdown, позволяет EXPLAIN: если в плане обращение к источнику уже содержит фильтр и агрегацию — pushdown сработал, источник вернёт маленький результат; если в плане отдельные узлы Filter и Aggregate после чтения источника — pushdown не сработал, и Trino тянет сырые строки. EXPLAIN ANALYZE дополнительно показывает фактический объём данных, прочитанный из источника.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Запрос SELECT count(*) FROM postgresql.public.orders WHERE status = 'completed' к таблице на 50 млн строк. Что произойдёт, если predicate и aggregation pushdown сработают?

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

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

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

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