Подтвердил скилл по оптимизации SQL запросов по максимуму :)

В Тинькофф, где сейчас работаю, хорошо выстроена система аттестации для подтверждения/повышения грейда. Не идеально - как я понял человек 20, кто как и я пришёл на роль бизнес-дата-инженера, с трудом вписываются в предлагаемые траектории развития. Но система понятная и в целом рабочая. Сегодня сдал скилл по оптимизации SQL запросов на третий (максимальный) уровень.

Всегда считал, что это у меня хорошо прокачано. Иногда необоснованно - как на первой работе, когда почти все проблемы решали через индексы (в Greenplum если вы используете индексы для оптимизации запросов - и это помогает - скорее всего вы зря используете именно Greenplum). Когда прошёл курс от Arenadatа (плюс постоянная практика в написании/оптимизации запросов) - вообще был уверен, что дальше расти некуда. Придя на работу в Тинькофф - прошёл внутренние курсы по оптимизации запросов - и снова был приятно удивлён. Год назад попытался пройти аттестацию (сразу замахнувшись с нуля на третий уровень) - и меня реально раскатали на экзамене, показав кучу нюансов в плане запроса, которые я упустил. Второй уровень честно засчитали, но с третьим было сказано больше тренироваться :) Летом упустил возможность пересдать, только сейчас снова открылось окно. Сдал :)

Доволен, задача красивая попалась. Правда, прямо на экзамене оказалось что у меня нет доступа в ту схему, где все таблицы (это как раз продолжение истории - все нормальные дата-инженеры, а я - бизнес-дата-инженер - что бы это ни значило, и права по автомату совсем другие). Но - опыта реально прибавилось, практически всю нужную инфу выцарапал из DDL таблиц и плана запроса (расширенный, по EXPLAIN Analyze).

Важный момент - НАДО вникать в бизнесовый смысл данных. Мне не нравится это, для меня комфортно, когда таблица это не бизнес-сущность, а строки с цифрами и символами. На уровне джуна и даже мидла - это допустимый подход, но если хочется расти (а я не представляю как может не хотеться - хотя и слышал про таких людей :) ), всё чаще приходится вникать именно что эти цифры значат для бизнеса. Вот и сейчас чуть не упустил красивую возможность подкрутить запрос.

В плане после кучи джойнов происходит Redistribute Motion того что получилось, для джойна данных из таблицы bank_info, при этом возникает дикий перекос - Avg 953961.7 rows x 143 workers at destination. Max 67442334 rows (seg104) - и проблемный сегмент не тот, куда падают null значения. Первая мысль была выделить проблемные значения sme_leg_transaction.counter_party_bank_rk (наиболее частые) в отдельную таблицу и прицепить к ним данные напрямую, но подсказали, обратили внимание на Join Filter и на то, что собственно читается из таблицы bank_info. Оттуда берётся только наименование банка - понятно что оно меняется крайне редко, т.е. всю кучу строк с разными значениями valid_from_dttm и valid_to_dttm можно схлопнуть до одной-двух строк для каждого банка, таблица bank_info в итоге сжимается до мизерного размера и через Broadcast Motion рассылается по всем сегментам, без необходимости менять дистрибьюцию основного массива данных.

  ->  Hash Left Join  (cost=0.00..676898.72 rows=444486497 width=421)
        Hash Cond: sme_leg_transaction.counter_party_bank_rk = bank_info.bank_rk
        Join Filter: sme_leg_transaction.real_transaction_dttm >= bank_info.valid_from_dttm AND sme_leg_transaction.real_transaction_dttm <= bank_info.valid_to_dttm
        Rows out:  Avg 953961.7 rows x 143 workers.  Max 67442334 rows (seg104) with 2813 ms to first row, 1486268 ms to end.
        Executor memory:  1283K bytes avg, 1523K bytes max (seg64).
        Work_mem used:  1283K bytes avg, 1523K bytes max (seg64). Workfile: (0 spilling)
        (seg9)   Hash chain length 124.5 avg, 131 max, using 139 of 262144 buckets.
        (seg64)  Hash chain length 124.7 avg, 130 max, using 159 of 262144 buckets.
        (seg104) Hash chain length 124.5 avg, 130 max, using 123 of 262144 buckets.
        ->  Redistribute Motion 144:144  (slice5; segments: 144)  (cost=0.00..19079.53 rows=3566091 width=397)
              Hash Key: sme_leg_transaction.counter_party_bank_rk
              Rows out:  Avg 953961.7 rows x 143 workers at destination.  Max 67442334 rows (seg104) with 2761 ms to first row, 67461 ms to end.

Да, если бы я шёл, не вникая, что за поле подключается, а просто смотрел бы количество уникальных значений - тоже вышел бы на это решение, но времени на это ушло бы больше. Нужно включать голову :)

З.Ы. Доволен, что сдал - наверное этим скиллом больше всего горжусь из всех. Надо ещё python сдать на третий уровень - тоже пока на втором завис. Проблема в том что некоторые темы - ООП всерьёз, асинхронка - вообще никогда не касался

Written on March 6, 2024