Оптимизации на основе стоимости#
Trino поддерживает несколько оптимизаций на основе стоимости, описанных ниже.
Перечисление join#
Порядок, в котором выполняются join в запросе, может существенно влиять на производительность запроса. Наибольшее влияние на производительность в порядке join оказывает объем данных, обрабатываемых и передаваемых по сети. Если join, который создает большой объем данных, выполняется на раннем этапе запроса, то последующим этапам приходится обрабатывать большие объемы данных дольше, чем необходимо, что увеличивает время и ресурсы, требуемые для обработки запроса.
При перечислении join на основе стоимости Trino использует Статистика таблиц, предоставляемую коннекторами, чтобы оценивать стоимость разных порядков join и автоматически выбирать порядок join с наименьшей вычисленной стоимостью.
Стратегия перечисления join управляется join_reordering_strategy
session property, при этом свойство конфигурации
optimizer.join-reordering-strategy задает значение по умолчанию.
Возможные значения:
AUTOMATIC(по умолчанию) - включает полное автоматическое перечисление joinELIMINATE_CROSS_JOINS- устраняет лишние cross joinNONE- исключительно синтаксический порядок join
Если вы используете перечисление join AUTOMATIC, а статистика недоступна
или стоимость не может быть вычислена по любой другой причине,
вместо этого используется стратегия ELIMINATE_CROSS_JOINS.
Выбор распределения join#
Trino использует hash-алгоритм join. Для каждого оператора join необходимо создать hash-таблицу по одному входу join, называемому build side. Затем итерируется второй вход, называемый probe side. Для каждой строки выполняется поиск в hash-таблице для нахождения совпадающих строк.
Существует два типа распределения join:
Partitioned: каждый узел, участвующий в запросе, строит hash-таблицу только по части данных
Broadcast: каждый узел, участвующий в запросе, строит hash-таблицу по всем данным. Данные реплицируются на каждый узел.
У каждого типа есть преимущества и недостатки. Partitioned join требуют перераспределения обеих таблиц с использованием hash от ключа join. Такие join могут быть значительно медленнее broadcast join, но в целом позволяют выполнять гораздо более крупные join. Broadcast join быстрее, если build side значительно меньше, чем probe side. Однако broadcast join требуют, чтобы таблицы на build side join после фильтрации помещались в память на каждом узле, тогда как distributed join должны помещаться только в распределенную память по всем узлам.
При выборе распределения join на основе стоимости Trino автоматически выбирает, использовать partitioned или broadcast join. При перечислении join на основе стоимости Trino автоматически выбирает, какие стороны будут probe и build.
Стратегия распределения join управляется session property
join_distribution_type, при этом значение по умолчанию задается
свойством конфигурации join-distribution-type.
Допустимые значения:
AUTOMATIC(по умолчанию) - тип распределения join определяется автоматически для каждого joinBROADCAST- для всех join используется broadcast распределениеPARTITIONED- для всех join используется partitioned распределение
Ограничение размера реплицируемой таблицы#
Тип распределения join выбирается автоматически, когда стратегия
переупорядочивания join установлена в AUTOMATIC, либо когда тип
распределения join установлен в AUTOMATIC. В обоих случаях можно
ограничить максимальный размер реплицируемой таблицы с помощью свойства
конфигурации join-max-broadcast-table-size или session property
join_max_broadcast_table_size. Это позволяет улучшить параллельность
в кластере и предотвратить плохие планы, когда оптимизатор на основе
стоимости ошибочно оценивает размер объединяемых таблиц.
По умолчанию размер реплицируемой таблицы ограничен 100MB.
Синтаксический порядок join#
Если оптимизация на основе стоимости не используется, Trino по умолчанию использует синтаксический порядок join. Хотя формального способа оптимизировать запросы для этого случая нет, можно воспользоваться тем, как Trino реализует join, чтобы сделать их более производительными.
Trino использует in-memory hash join. При обработке оператора join Trino загружает в память самую правую таблицу join как build side, затем потоково обрабатывает следующую справа таблицу как probe side для выполнения join. Если в запросе несколько join, результат первого join остается в памяти как build side, а третья справа таблица используется как probe side, и так далее для дополнительных join. В случаях, когда порядок join усложняется, например при использовании скобок для задания конкретных групп join, Trino может выполнять несколько join нижнего уровня одновременно, но каждый этап этого процесса следует той же логике; то же самое применимо и когда результаты в итоге объединяются между собой.
Из-за такого поведения оптимально синтаксически располагать join в ваших SQL- запросах от самых больших таблиц к самым маленьким, так как это минимизирует использование памяти.
Например, если у вас есть маленькая, средняя и большая таблицы, и вы используете left join:
SELECT
*
FROM
large_table l
LEFT JOIN medium_table m ON l.user_id = m.user_id
LEFT JOIN small_table s ON s.user_id = l.user_id
Warning
Этот способ оптимизации не является возможностью Trino. Это побочный эффект того, как реализованы join, и поэтому такое поведение может измениться без предупреждения.
Реализации коннекторов#
Чтобы оптимизатор Trino мог использовать стратегии на основе стоимости, реализация коннектора должна предоставлять Статистика таблиц.