Перейти к содержанию
Learning Platform
Глоссарий
Troubleshooting

Troubleshooting — Trino

База знаний типичных ошибок курса Trino.

Показано 32 из 32 ошибок

Причина

Распространённое заблуждение. Trino — распределённый SQL query engine, а не СУБД: своего хранилища у него нет. Данные живут в подключённых источниках, Trino только исполняет SQL над ними.

Решение

  1. Перестройте ментальную модель: Trino — это compute-слой, хранилище — отдельно (object storage, RDBMS, Kafka). Каталог описывает подключение к источнику. Нет таблиц «внутри Trino» — есть catalog.schema.table, указывающие на внешние данные. Для постоянного хранения данных нужен коннектор с записью (Iceberg, Delta, Hive).

Причина

Trino спроектирован под OLAP — тяжёлую аналитику, сканы, агрегации. Это не OLTP-система: нет транзакций уровня БД, нет индексов в традиционном смысле, точечные SELECT/UPDATE по ключу неэффективны.

Решение

  1. Для транзакционной нагрузки оставьте OLTP-СУБД (PostgreSQL, MySQL). Trino подключайте к ней коннектором ради аналитики и федерации, а не вместо неё. Признак неправильного применения: много мелких быстрых запросов по первичному ключу — это не сценарий Trino.

Причина

Запрос превысил query.max-memory-per-node — лимит user memory на одной ноде (по умолчанию 30% максимального heap). Частая первопричина — broadcast join, где build-side не помещается в память воркера.

Решение

  1. Снимите EXPLAIN ANALYZE, найдите тяжёлый оператор. Соберите статистику через ANALYZE, чтобы CBO выбрал PARTITIONED join вместо BROADCAST. Принудительно: SET SESSION join_distribution_type = 'PARTITIONED'. Включите spill для соответствующих операторов. Как крайняя мера — поднимите query.max-memory-per-node, не нарушая ограничение: max-memory-per-node + heap-headroom-per-node < -Xmx.

Причина

При BROADCAST build-side (правая таблица) копируется целиком на каждую ноду как хэш-таблица. Если build-side больше join-max-broadcast-table-size (по умолчанию 100MB) или статистики нет, выбор BROADCAST приводит к OOM.

Решение

  1. Убедитесь, что меньшая таблица стоит build-side. Соберите статистику (ANALYZE) — тогда AUTOMATIC сам выберет PARTITIONED для больших таблиц. Принудительно переключите: SET SESSION join_distribution_type = 'PARTITIONED'. PARTITIONED перераспределяет обе таблицы по хэшу и использует суммарную память кластера.

Причина

Нет актуальной статистики таблиц. Без row count и NDV оптимизатор не может оценить кардинальность; join-reordering-strategy AUTOMATIC при отсутствии статистики откатывается к ELIMINATE_CROSS_JOINS и фактически идёт по синтаксическому порядку.

Решение

  1. Выполните ANALYZE для участвующих таблиц, проверьте результат через SHOW STATS FOR <table> — там должны быть непустые row count и NDV. Для Iceberg/Delta статистика обновляется при DML, для Hive часто нужен явный ANALYZE. После сбора статистики сравните план EXPLAIN до и после.

Причина

Статистика не собиралась, либо данные изменили в обход Trino (внешний загрузчик дописал файлы), либо коннектор не поставляет нужные метрики (исторически Hive не отдавал data size).

Решение

  1. Запустите ANALYZE <table>. Если данные регулярно меняются вне Trino — включите ANALYZE в pipeline загрузки. Учтите, что полнота статистики зависит от коннектора: проверьте документацию конкретного коннектора, какие метрики он поддерживает.

Причина

По умолчанию Trino stateless и НЕ отказоустойчив: retry-policy=NONE. Сбой любого воркера, участвующего в запросе, означает падение всего запроса. Это by-design, а не баг.

Решение

  1. Для длинных batch-запросов включите fault-tolerant execution: retry-policy=TASK (ретрай отдельных задач) плюс настроенный exchange manager в etc/exchange-manager.properties. Для кластеров с множеством коротких запросов подойдёт retry-policy=QUERY. FTE покрывает инфраструктурные сбои, но не ошибки SQL.

Причина

Заблуждение «Trino отказоустойчив как Spark». По умолчанию это не так: stateless-дизайн, сбой воркера = падение запроса. Отказоустойчивость даёт только опциональный FTE.

Решение

  1. Если нужна устойчивость к сбоям нод (особенно для долгих ETL-запросов) — осознанно включите FTE с retry-policy=TASK и exchange manager, желательно на выделенном кластере. Понимайте trade-off: FTE добавляет латентность коротким запросам из-за спулинга промежуточных данных.

Причина

Режим TASK требует настроенного exchange manager для спулинга промежуточных данных. Без etc/exchange-manager.properties на всех нодах FTE в режиме TASK не работает.

Решение

  1. Создайте etc/exchange-manager.properties на всех нодах: exchange-manager.name=filesystem и exchange.base-directories с указанием на надёжное хранилище (S3, GCS, Azure Blob, HDFS). Локальную ФС для спула используйте только не в проде. Перезапустите кластер.

Причина

Не сработало dynamic filtering: оно собирает значения join-ключей с отфильтрованной build-side и проталкивает их в scan fact-таблицы. Условие может быть неприменимым типом join, либо build-side слишком велик.

Решение

  1. Проверьте план: dynamicFilterAssignments в join-нодах и dynamicFilters в scan, в Web UI — dynamicFiltersStats. Dynamic filtering работает для INNER/RIGHT join с операторами сравнения и semi-join с IN. Убедитесь, что dimension-таблица реально мала после фильтра. min/max не поддерживается для DOUBLE и REAL.

Причина

Частые мелкие записи (стриминговые вставки, частые INSERT) порождают тысячи маленьких файлов. Это бьёт по планированию (раздувается число сплитов) и по дисковому I/O.

Решение

  1. Для Iceberg выполните ALTER TABLE <table> EXECUTE optimize — компакция мелких файлов в крупные (параметр file_size_threshold). Для Delta Lake — процедура optimize. Делайте это регулярно как обслуживание. Снизьте частоту записи на стороне загрузчика, накапливая батчи побольше.

Причина

Iceberg и Delta хранят историю: старые снапшоты и заменённые файлы данных остаются ради time travel. Без обслуживания они накапливаются бесконечно.

Решение

  1. Для Iceberg: ALTER TABLE EXECUTE expire_snapshots (удаляет старые снапшоты, retention по умолчанию 7 дней) и remove_orphan_files (файлы вне снапшотов). Для Delta: процедура VACUUM. Настройте обслуживание по расписанию, согласовав retention с реальными требованиями к time travel.

Причина

Координатор парсит, планирует и управляет всеми запросами кластера в одиночку. Много конкурентных запросов, тяжёлое планирование или включённый node-scheduler.include-coordinator (координатор работает ещё и воркером) перегружают его.

Решение

  1. Не назначайте координатор воркером в нагруженном кластере: node-scheduler.include-coordinator=false. Масштабируйте воркеры, а не координатор. Введите resource groups, чтобы ограничить число одновременных запросов. Для нескольких кластеров поставьте Trino Gateway и распределяйте нагрузку.

Причина

Жёстко зафиксированный BROADCAST вызывает OOM на больших таблицах; жёсткий PARTITIONED добавляет лишний шаффл там, где хватило бы broadcast маленькой таблицы.

Решение

  1. В большинстве случаев оставьте join-distribution-type=AUTOMATIC — при наличии статистики CBO выберет верную стратегию сам. Меняйте на конкретное значение только осознанно, для отдельного запроса через SET SESSION join_distribution_type, и проверяйте эффект по EXPLAIN ANALYZE.

Причина

Файл каталога etc/catalog/<name>.properties создан с ошибкой: отсутствует обязательное connector.name, опечатка в имени свойства, либо файл добавлен без перезапуска (на старых версиях каталоги статичны).

Решение

  1. Проверьте, что в файле есть connector.name=<connector> и все обязательные свойства этого коннектора. Сверьте имена свойств с документацией коннектора. Перезапустите кластер либо используйте механизм динамических каталогов, если он включён. Имя каталога = имя файла без .properties.

Причина

В catalog properties file заданы неправильные connection-параметры: URL/host, логин, пароль, путь к метастору. Trino не может установить соединение с источником.

Решение

  1. Проверьте параметры подключения в etc/catalog/<name>.properties (например, connection-url, connection-user для JDBC; hive.metastore.uri для lakehouse). Чувствительные значения держите через механизм secrets, а не в открытом виде. Проверьте сетевую доступность источника с нод кластера.

Причина

Заблуждение о природе скорости. Trino не материализует датасеты целиком и не является in-memory СУБД. Данные стримятся через операторы.

Решение

  1. Поймите реальные источники скорости: MPP-параллелизм, pipelined-исполнение, векторизованные колоночные операторы (Page/Block), pushdown в источник и dynamic filtering. Оптимизируйте именно их: собирайте статистику, проверяйте pushdown в EXPLAIN, следите за форматами и размерами файлов.

Причина

Путаница в таксономии исполнения. Stage — концептуальная фаза распределённого плана, она не исполняется напрямую. На воркерах исполняются task'и — реализации стадии.

Решение

  1. Запомните иерархию: stage -> task -> split -> driver -> operator. Стадия распараллеливается на множество задач на разных воркерах; задача обрабатывает сплиты через драйверы. При чтении EXPLAIN ANALYZE и Web UI смотрите на задачи и драйверы, чтобы понять реальное исполнение.

Причина

По умолчанию координатор не исполняет задачи с данными — он только парсит, планирует и управляет. Это нормально и снимает с него нагрузку обработки.

Решение

  1. Если хочется задействовать координатор как воркер (оправдано только в маленьких кластерах для разработки) — установите node-scheduler.include-coordinator=true. В проде не делайте этого: координатор должен быть свободен для планирования и управления.

Причина

Воркеры не зарегистрировались в discovery service: неверный discovery.uri в config.properties, рассинхрон node.environment между нодами, или сетевая недоступность координатора.

Решение

  1. Проверьте, что discovery.uri на воркерах указывает на координатор. node.environment в etc/node.properties должен быть ОДИНАКОВ на всех нодах, а node.id — уникален. Откройте Web UI координатора и убедитесь, что воркеры появились в списке нод. Проверьте сетевую связность и порт.

Причина

Не каждый коннектор реализует pushdown полностью. Если applyFilter/applyProjection/applyAggregation не поддержаны для данного предиката или типа, фильтрация и проекция выполняются уже в Trino отдельным оператором.

Решение

  1. Снимите EXPLAIN: при сработавшем pushdown предикат виден прямо в TableScan, а не отдельным Filter-оператором. Сверьте с документацией коннектора, какие виды pushdown он поддерживает. Иногда мешает выражение, которое источник не понимает — упростите предикат или приведите типы.

Причина

Превышен query.max-cpu-time — суммарный лимит CPU-времени на запрос по кластеру. По умолчанию он практически бесконечен, значит лимит явно ужесточён в конфиге или resource group.

Решение

  1. Проверьте query.max-cpu-time в config.properties и настройки resource groups. Если запрос легитимно тяжёлый — оптимизируйте его (статистика, pushdown, партиционирование) либо поднимите лимит для соответствующей группы. Если это runaway-запрос — лимит работает правильно, чините сам запрос.

Причина

Нет изоляции нагрузки: все запросы делят ресурсы кластера на равных. Тяжёлый ad-hoc-скан забивает кластер и тормозит лёгкие запросы дашбордов.

Решение

  1. Настройте resource groups: отдельные группы для dashboards и adhoc с разными лимитами памяти, CPU и приоритетами. Это разводит нагрузки по очередям. Альтернативно — отдельные кластеры под разные нагрузки за Trino Gateway с маршрутизацией по правилам.

Причина

Spill поддержан не для всех операторов (работает для aggregations, joins, sort, window functions) и не безграничен: упирается в max spill space или в disk I/O. Иногда память расходует оператор, который спилить нельзя.

Решение

  1. Проверьте, что узкий оператор спиллируемый. Убедитесь, что директория спила задана, имеет место и НЕ совпадает с системным диском или диском JVM-логов; можно указать несколько дисков через запятую. Если spill упирается в I/O — параллельно решайте проблему планом: статистика, PARTITIONED join, меньше данных через pushdown.

Причина

Заблуждение, что Iceberg/Delta/Hive — это базы данных. Это форматы таблиц поверх файлов; источник истины — метаданные в каталоге/метасторе. Файлы, добавленные мимо движка без коммита в метаданные, для таблицы не существуют.

Решение

  1. Загружайте данные операциями, которые обновляют метаданные таблицы (INSERT/CTAS через Trino или другой движок с поддержкой формата). Для Hive с внешне добавленными партициями — процедура system.sync_partition_metadata. Не дописывайте файлы в каталог таблицы напрямую в обход формата.

Причина

Заблуждение «Trino = Presto». PrestoSQL был переименован в Trino 27 декабря 2020. Сегодня существуют два разошедшихся форка: PrestoDB (Meta, Linux Foundation) и Trino (бывший PrestoSQL).

Решение

  1. Учитывайте, что это разные проекты с общим прошлым: разные репозитории, релизы, фичи. Документация, версии и синтаксис различаются. Для этого курса ориентир — Trino и его документация trino.io; материалы по PrestoDB напрямую не применимы.

Причина

Trino давно ушёл от модели отдельных memory pools (general и reserved). Сейчас единая модель: user memory, system memory, revocable memory. В устаревших статьях и старых изданиях книги встречается «reserved pool».

Решение

  1. Опирайтесь на актуальную модель памяти и свойства: query.max-memory, query.max-memory-per-node, query.max-total-memory, memory.heap-headroom-per-node. Игнорируйте советы про настройку memory pools — их больше нет. Сверяйте конфигурацию с документацией текущего релиза.

Причина

В релизе 481 удалён legacy-слой object storage (старые Hadoop-based реализации) в коннекторах Hive/Delta/Iceberg/Lakehouse. Актуальна только новая нативная файловая система (native S3/Azure/GCS).

Решение

  1. Переведите конфигурацию каталогов на нативную файловую систему: свойства native S3/Azure/GCS file system вместо устаревших Hadoop-настроек. Сверьтесь с release notes 481 (ломающие изменения помечены значком предупреждения) и с актуальной страницей коннектора object storage.

Причина

Не хватает навыка чтения распределённого плана: какая стадия дорогая, что распределяется как, где теряется время. Базовый EXPLAIN ANALYZE не показывает низкоуровневую статистику операторов.

Решение

  1. Снимите EXPLAIN ANALYZE VERBOSE и смотрите CPU-время по стадиям и объём данных между ними. Помните: в выводе меньший номер стадии — последний шаг, больший — первый. Ищите стадию с наибольшим CPU и input. Дальше — статистика через ANALYZE, проверка join order, dynamic filtering и pushdown в плане.

Причина

Fault-tolerant execution покрывает только инфраструктурные сбои (падение воркера, сетевые проблемы). Ошибки пользователя — синтаксис SQL, несуществующая таблица, неверный тип — не ретраятся, это by-design.

Решение

  1. Исправьте сам запрос: ошибки SQL FTE не лечит. Ретраи (query-retry-attempts, task-retry-attempts-per-task) применяются только к инфраструктурным сбоям. Если запрос стабильно падает с одной и той же ошибкой — это не кейс для FTE.

Причина

Большинство методов аутентификации (PASSWORD, LDAP, OAUTH2, KERBEROS, JWT) требуют TLS/HTTPS и настроенного shared secret. Без них координатор отклоняет аутентификацию.

Решение

  1. Включите TLS на координаторе и настройте shared secret для внутренней коммуникации. Для OAuth2 TLS на координаторе обязателен. Если перечислено несколько типов аутентификации — они проверяются по порядку, первый успех даёт доступ; убедитесь, что нужный тип в списке и корректно сконфигурирован.

Причина

Антипаттерн федерации: огромная таблица из RDBMS целиком вытягивается в Trino, потому что pushdown не сократил объём. Узким местом становится JDBC-источник и сеть.

Решение

  1. Проверьте по EXPLAIN, что Trino протолкнул в RDBMS predicate/aggregation pushdown — источник должен вернуть меньше строк. Фильтруйте данные на стороне источника, не тяните всю таблицу. Если RDBMS-таблица большая и часто нужна для аналитики — рассмотрите её материализацию в lakehouse вместо живой федерации на каждый запрос.