Архитектурный принцип Lakehouse предполагает, что вы оперируете всеми данными, загруженными в систему. Но иногда нужно выполнить ad hoc анализ за ее периметром, потому что необходимых данных по каким-либо причинам нет в Lakehouse-платформе. В этом случае на помощь приходит федеративный доступ. Стандартом для такой задачи является движок Trino. Он умеет извлекать данные из внешних СУБД и даже в некоторых случаях может делать push-down определенных вычислений на сторону системы-источника. Главное, чтобы под рукой был подходящий connector для нужной СУБД, который умеет эффективно с ней работать.

Недавно в состав Data Ocean Nova был добавлен новый Trino Teradata Connector. Он позволяет пользователям «подтягивать» необходимые срезы данных из Teradata в рамках ad hoc запросов и решает задачу эффективной передачи данных: можно передавать терабайты в несколько потоков без существенного увеличения нагрузки на источник.
В данной статье разберем:
Как организовать эффективную многопоточную работу с Teradata:
Где часто допускают ошибки;
Как должно выглядеть правильное решение;
Какие возможности дает Nova Trino Teradata Connector:
Многопоточная передача;
Push-down оптимизации.
В качестве вступления
Для начала стоит отметить, что мы не рекомендуем использовать функционал Nova Trino Teradata Connector именно для задач миграции данных в Lakehouse-платформу или для регулярной загрузки. Для этого у нас есть специализированный инструмент эффективной пакетной передачи данных Data Ocean Flex Loader, который при работе с Teradata использует (по выбору пользователя или администратора) либо механизм Native Object Storage (NOS), либо Teradata Parallel Transporter (TPT). Проблема доступа посредством Trino у наших клиентов, как правило, возникает в федеративных прикладных задачах вроде реконсиляции данных, реализации приложений проверки качества, профилирования данных системы-источника и так далее.
Эффективный клиентский доступ к Teradata
Небольшое погружение в архитектуру Teradata
Среди целей этой статьи нет детального погружения в СУБД Teradata, но, чтобы остальные технические детали были понятны и общий пазл по итогам прочтения сложился в понятную картину, кратко затронем ключевые архитектурные аспекты. Думаю, гораздо больше читателей могут быть знакомы с Greenplum, поэтому также проведем параллели с ним.

Teradata — это MPP-СУБД по схеме shared-nothing: каждый рабочий узел имеет свой выделенный объем вычислительных ресурсов (CPU, RAM, Disk) для решения задач. Рабочие узлы здесь именуются AMP’ами (Access Module Processor, полное имя используется редко), записи в таблицах распределяются между AMP’ами для хранения и обработки. AMP’ы объединены в общую interconnect-сеть BYNET. Пользователи подключаются и отправляют запросы к компонентам Parsing Engine (PE) — именно здесь происходит парсинг запросов, построение плана запроса, формирование задач для AMP’ов, контроль выполнения, формирование и отдача ответа клиенту.
Teradata проектировалась очень давно, оперативной памяти было значительно меньше, и она была значительно дороже (хотя, публикуя эту статью в начале 2026 г., рассуждать о дешевой памяти, к сожалению, не приходится). Поэтому СУБД изначально активно использовала дисковую подсистему для сохранения промежуточных вычислений и подготовленных данных (result sets) — это решение работает до сих пор. В планах запросов можно увидеть, что очень часто СУБД будет писать в Spool — это оно и есть.
Для тех, кто знаком с Greenplum, быстро проведем аналогию:
Master Server → Parsing Engine (PE), которых может быть несколько; каждый PE может обрабатывать порядка 100+ сессий;
Segment → AMP. Обычно на одном достаточно мощном физическом Segment Host поднимают несколько Segment Server’ов, но на аналогичном по мощности Teradata Node всегда больше AMP’ов, поэтому обычно в Teradata общее количество AMP’ов больше, и счет идет на сотни и тысячи. Но и каждый AMP имеет в своем распоряжении меньше ресурсов и хранит/обрабатывает меньше данных, чем один Segment;
Distributed Key → Primary Index. Данные распределяются между рабочими узлами по hash’у от полей, входящих в ключ дистрибуции. Обычно стараются добиться равномерного распределения данных между рабочими узлами на больших таблицах (для маленьких таблиц обычно выбирается другая стратегия хранения данных). В деталях реализации механизма дистрибуции данных (как и хранения) есть немало отличий, но в них сейчас погружаться не будем. Поставим в приоритет основные цели статьи;
Spill → Spool. В отличие от Greenplum, который старается оставить вычисления в RAM, пока не исчерпает ее, Teradata сразу будет писать в Spool.
Где часто допускают ошибку
За свою практику мы сталкивались с несколькими решениями для работы с Teradata и последующей загрузки в Hadoop или Lakehouse, и у многих были одни и те же проблемы с эффективностью извлечения данных из Teradata. И не только потому, что некоторые выгружали сотни ГБ в один поток. Критичнее, когда выгрузка в N потоков создает N-кратную нагрузку на исходную систему.
Для параллельной выгрузки в таких случаях формировалось несколько запросов, каждый из которых должен обработать свою часть записей (есть две ключевые версии):
SELECT ... FROM table WHERE hashbucket(hashrow(key_field)) MOD 4 = 0; SELECT ... FROM table WHERE hashbucket(hashrow(key_field)) MOD 4 = 1; SELECT ... FROM table WHERE hashbucket(hashrow(key_field)) MOD 4 = 2; SELECT ... FROM table WHERE hashbucket(hashrow(key_field)) MOD 4 = 3;
SELECT ... FROM table WHERE hashamp(hashbucket(hashrow(key_field))) MOD 4 = 0; SELECT ... FROM table WHERE hashamp(hashbucket(hashrow(key_field))) MOD 4 = 1; SELECT ... FROM table WHERE hashamp(hashbucket(hashrow(key_field))) MOD 4 = 2; SELECT ... FROM table WHERE hashamp(hashbucket(hashrow(key_field))) MOD 4 = 3;
И каждый из этих запросов действительно вернет свою часть записей, которые не будут пересекаться между собой (сейчас оставим за скобками кейс с параллельными изменениями для упрощения). Но что для этого пришлось сделать СУБД, какую работу проделать?
Для обработки каждый AMP сканирует все записи и проверяет, для каких выполняется условие hash(key_field) MOD 4 = X. Такие записи каждый AMP записывает в Spool. Затем каждый AMP отдает эти записи из Spool’а клиенту. Другими словами, во время одного такого запроса мы сканируем таблицу целиком, тратим дополнительное CPU на вычисления предикатов для всех строк, и только потом начинается хоть какая-то экономия ресурсов.

Но у нас будет не один такой запрос, их будет N штук. Т.е. выгрузка в N потоков приведет к N full table scan: увеличивая количество потоков, мы будем линейно увеличивать нагрузку на исходную систему. Хотим выгрузить 10 ТБ таблицу из Teradata в Lakehouse, но в 1 поток выходит как-то медленно? Поставим 10 потоков и заставим сделать работы в 10 раз больше, т.е. вместо сканирований на 10 ТБ через I/O-подсистему прогоним 100 ТБ + потратим кучу CPU для вычислений предикатов.
Как итог, данный метод очень плохо масштабируется, выгрузка в N потоков дает:
