Производительность
Почему так долго работает запрос с JOIN?
Все основные виды JOIN (кроме SEMI/ONLY) строят декартово произведение строк по совпадающим значениям ключей. Если одно или несколько значений ключа являются особенными, то по нему может оказаться большое число строк с каждой из сторон и, как следствие, гигантское декартово произведение. В большинстве случаев JOIN работает медленно именно из-за этого, а такие особые значения с прикладной точки зрения оказываются не так интересны, и достаточно их исключить в WHERE.
Найти аномально часто встречающиеся значения можно с помощью двух запросов по каждой из таблиц с GROUP BY по тем же ключам, что и у JOIN, используя агрегатную функцию COUNT и убывающую сортировку по её результату. Ещё можно добавить LIMIT 100, чтобы отработало ещё быстрее.
Также бывают запросы, где на самом деле декартово произведение в JOIN не нужно, т. к. потом всё равно от значений тем или иным способом оставляют только уникальные значения. В этом случае стоит убрать дубли в подзапросе через GROUP BY или SELECT DISTINCT ещё до JOIN.
Мой запрос раньше работал за N часов, а теперь стал работать за M часов. В чем причина?
При анализе производительности стоит обратить внимание на следующие моменты:
Поменялся ли входной объём данных
Например, в запрос стало попадать больше таблиц или существенно изменился их объём. Чтобы проверить, поменялся ли входной объём данных, можно кликнуть в плане запроса на первую YTsaurus операцию и посмотреть в интерфейсе YTsaurus на Data flow/Data weight:

Поменяли ли план исполнения запроса
Если план изменился, то это могло произойти по нескольким причинам:
- Стала применяться другая стратегия
JOIN. Про критерии срабатывания той или иной стратегии смотри отдельную статью. - Изменилась схема некоторых входных таблиц в папке. YQL выравнивает схему входных таблиц, что приводит к появлению дополнительных операций.
- Поменялись оптимизаторы YQL с релизом новой версии.
Используется ли в запросе пул с выделенными гарантиями
В запросе должна быть указана прагма yt.StaticPool для применения пула. Если её нет, то время исполнения запроса никак не гарантируется. На него могут повлиять как соседние запросы, так и сезонные колебания нагрузки на кластера (перед ревью нагрузка на кластера, как правило, возрастает). Также стоит убедиться, что пул задаётся прагмой yt.StaticPool, а не yt.Pool. Последняя является динамической прагмой, применяемой уже в рантайме. Из-за этого служебные операции, запущенные на этапе оптимизации, не используют её значение и запускаются в пуле по умолчанию, что может приводить к их существенному замедлению. Прагма yt.StaticPool применяется для всех операций, в том числе и служебных.
Проверка суммарных статистик запроса
На вкладке Statistics YQL-запроса можно посмотреть его суммарные статистики:

Здесь в первую очередь нужно обратить внимание и сравнить значения следующих статистик:
yt/total/Job_CalcTime(Sum) — полезное время, проведенное во всех джобах с пользовательским кодом всех YTsaurus-операций запроса. Изменение этого значения при отсутствии изменения входного объёма данных говорит об изменениях производительности в YQL-рантайме.yt/total/Job_InputDecodeTime(Sum) — время, потраченное YQL декодером на чтение входных данных во всех джобах всех YTsaurus-операций запроса. Изменение этого значения при отсутствии изменения входного объёма данных говорит об изменениях производительности в YQL декодере.yt/total/Job_OutputEncodeTime(Sum) — время, потраченное YQL энкодером на запись данных во всех джобах всех YTsaurus-операций запроса. Изменение этого значения при отсутствии изменения входного объёма данных говорит об изменениях производительности в YQL энкодере.yt/total/CodeGen_FullTime(Sum) — время, потраченное на кодогенерацию во всех джобах всех YTsaurus-операций запроса. Изменение этого значения при том же числе YTsaurus-джобов говорит об изменениях производительности кодогенерации YQL-рантайма. Если это время составляет существенную долю отJob_CalcTime, то стоит рассмотреть вариант отключения LLVM в запросе черезpragma config.flags("LLVM", "OFF").yt/total/Join_Spill_Count(Sum) — ненулевое значение этой статистики говорит о проблемах производительностиJOINв запросе. Если ключи по первой стороне джойна имеют очень много записей, то они не помещаются в памяти и сохраняются на диск. Это приводит к существенной деградации времени YTsaurus-джобов, выполняющихJOIN. Для решения этой проблемы можно увеличить значение прагмы yt.CommonJoinCoreLimit, либо поменять местами стороныJOIN.yt/total/data/input/data_weight(Sum) — суммарный объём данных, обработанный всеми джобами всех YTsaurus-операций запроса. При отсутствии изменений в плане запроса, изменение этой статистики говорит об изменении входного объёма данных запроса.
Поиск самой медленной YTsaurus-операции запроса
На вкладке Progress YQL-запроса нужно переключиться в режим Timeline:

В этом режиме наглядно видно, какая YTsaurus-операция больше всего повлияла на время исполнения YQL-запроса. Можно также раскрыть детали исполнения разных стадий операции:

Здесь видно, что операция долго стояла в статусе Pending, т. е. ей не хватало лимитов на число работающих операций в пуле.
Для дальнейшего анализа можно открыть медленную операцию в YTsaurus интерфейсе, кликнув на её название в левой колонке timeline. В YTsaurus-операции в первую очередь стоит обратить внимание на Total job wall time и Total CPU time spent. Если эти значения для двух запусков YQL-запроса существенно не изменились, но при этом изменился Duration, то это сигнализирует о проблемах в пуле запроса. Нужно кликнуть на название пула

и открыть его в интерфейсе YTsaurus на вкладке Monitoring для анализа его нагрузки.