Вредные советы при построении Аналитики (Data Lake / DWH / BI) – чего стоит избегать
Всем привет! На связи Артемий, со-автор и преподаватель курсов Data Engineer, DWH Analyst.
Последние месяцы я много занимаюсь рефакторингом кодовой базы, оптимизацией процессов и расчетов в сфере Анализа Данных.
Появилось желание в формате «вредных советов» обратить внимание на набор практик и подходов, которые могут обернуться весьма неприятными последствиями, а порой и вовсе дорого обойтись Вашей компании.
В публикации Вас ожидает:
Использование
select *
— всё и сразуУпотребление чрезмерного количество CTEs (common table expressions)
NOT DRY (Don«t repeat yourself) — повторение и калейдоскопический характер расчетов
Использование select * — всё и сразу
Начнем с банального — селект звёздочка. Комадна, с которой многие аналитики начинают свой путь в SQL. Спору нет — это прекрасный способ, чтобы начать исследовать таблицы, которые Вы можете видеть впервые, однако также это потенциально очень опасная практика в поставке PRODUCTION-кода.
Почему это важно?
1. С использованием сокращения select *
вы теряете конкретику и прозрачность
А значит, для ответа на вопрос, используется ли здесь какой-то конкретный атрибут, придется, как минимум копнуть глубже и сделать 2–3–4 шага назад.
2. Хрупкость выстраиваемой архитектуры
Если нижележащие зависимости исходят из предположения наличия заданного набора атрибутов, то вся схема будет работать ровно до того момента, когда кто-либо как-либо не изменит этот набор. И, скорее всего, при этом он даже не будет хотеть что-то сломать.
3. Не стоит тянуть все имеющиеся колонки
Тем самым вы нивелируете преимущества Анлитических СУБД, которые хранят данные в виде колонок, не строк! Да, мы сейчас про Clickhouse, Snowflake, Redshift, BigQuery, Databricks, Greenplum и многие другие.
Просто посмотрите анимацию из документации Clickhouse:
Что может пойти не так?
1. Зависимости, интеграции, BI, просто Python-скрипты могут падать с ошибкой
Посмотрите на интеграцию Хранилища с Airtable. Структура таблицы-приемника была однозначно зафиксирована в момент первой вставки (INSERT) и последующие записи (UPDATE + INSERT) с ранее неизвестными атрибутами возвращают ошибки.
2. Появление лишней, ненужной, запретной информации там где её быть не должно
Для узкоспецилизированной задачи нет смысла выгружать полный набор атрибутов широкой таблицы-справочника. В данном случае это Chauffeur Onboarding в Airtable и большое количество колонок таблицы скорее мешает восприятию и обработке информации.
В иных случаях в обзор могут случайно попасть данные, содержащие тайну или персональные данные. Как сказал один мой коллега в Wheely:
Here in IT we love Least Privilege Principle
Что делать?
Явно указывайте минимально необходимый набор атрибутов. Строк кода будет немного больше, но выше станет и надежность этого кода.
Однако, как и всё остальное, однозначно отнести эту практику к негативным нельзя. Возможно, в Вашем случае вероятность FAIL«ов и влияние на downstream-зависимости будет минимальным, и такой подход сможет заметно ускорить и улучшить процесс разработки и поставки аналитических сервисов. Моя задача — предупредить и обратить внимание.
Чрезмерное количество CTEs (common table expressions) в логике трансформаций
CTE — это практика написания транформаций, когда вместо того чтобы городить несколько уровней подзапросов, достаточно определить их в отдельные табличные выражения и в этом же самом запросе ссылаться на них. Что-то типа alias, но для наборов строк.
Посмотрите пример красивого использования CTE в модели dbt: https://gist.github.com/kzzzr/5cccc74f6d9eeb189ae6fdba1b2ec14a
Да, это удобно. Да, это способствует структурированному решению задачи и пошаговой реализации логики. Но это не повод злоупотреблять доступной возможностью.
Почему это важно?
1. Растущая сложность
Лучше упрощать, чем усложнять. Избавляться от лишних и ненужных частей, чем хранить их и пытаться вписывать новые требования в старые рамки.
2. Неэффективное исполнение запросов с огромным количеством CTE
Как правило, запрос исполняется следующим образом:
Последовательно собираются все CTE (возможно, записываются на диск)
Выполняется финальный запрос (который, возможно, объединяет все CTE)
А это значит, что сначала будут собраны все CTE, и только потом мы начнем их фильтровать и отбрасывать ненужные колонки. Даже если у вас нет ограничений по CPU, запрос может исполнятся долго (и дорого!) из-за большого объема сканируемых данных (I/O). Есть умные движки и парсеры запросов, которые переписывают код и максимально фильтруют данные, но рассчитывать на них не следует.
Что может пойти не так?
1. Появление участков-бутылочных горлышек (bottlenecks)
41 подзапрос? Запросто!
2. Изменения в таких участках кода происходят путем добавления новых CTE
Как правило, аналитики стоят перед выбором: с одной стороны — разобраться во всей цепочке CTE-выражений и внести изменения там, где правильно, с другой стороны — просто добавить свой код поверх.
В условиях множества задач и желания получить быстрый результат ответ очевиден — просто добавляем еще несколько CTE (ведь хуже уже не будет?).
3. Это трудно рефакторить
Кодовая база становится похожей на спагетти-код. Становится сложно понять, что из чего следует, и зачем здесь эти участки кода.
Реальный пример плана запроса (может быть больно для Вашего мозга!): https://gist.github.com/kzzzr/6499510ac7fa0004fd32ed30e1df4541
Спрячу его под спойлер
Пожалею Вас и не буду показывать сам запрос.
Что делать?
Решать задачу за минимум операций и шагов (keep it simple).
Не тянуть лишние строки и колонки (filter early)
Не повторять одни и те же операции (см. след пункт!)
Поймите, CTE — не пацанея. Это просто способ красиво оформить код сложного набора преобразований. Добавление новых и новых CTE будет только усугублять ситуацию.
NOT DRY (Don«t repeat yourself) — повторение и калейдоскопический характер расчетов
В условиях распределенной команды, разделения на зоны ответственности, бизнес-вертикали, так или иначе возникает ситуация, когда аналитики будут делать одни и те же вещи. И каждый своим способом.
Почему это важно?
1. Одна версия правды
Это важно для бизнес-метрик. Разные подходы, формулы, реализации расчета одного и того же показателя приводят бизнес-пользователей в ступор.
2. Одна точка для изменений и эволюции кода
Если Вам понадобилось поменять формулу расчета (добавить слагаемое, изменить налоговых коэффициент, учесть новую группу пользователей, …) — это нужно будет сделать только в одном месте, а не в нескольких.
3. Оптимальный код
Если Вы часто обращаетесь к одному и тому же набору данных — есть смысл материализовать его в виде промежуточного набора данных (таблицы).
Проведите аналогию с вызовом функции или метода в языке программирования, только здесь — набор данных.
Что может пойти не так?
1. Дублирование одной и той же бизнес-логики в разных местах
Изменили в одном месте, но забыли в другом — получите баг.
2. Излишняя нагрузка на СУБД
А равно и трата вычислительных ресурсов, за которые Вы платите.
3. Рост объема устаревшего и ненужного кода
20–30% — в среднем такова моя оценка доли legacy в проекте, которая, увы, уже вряд ли когда-то будет востребована.
Есть процессы разработки и поставки новых витрин, но пока нет процессов Garbage Collection — удаления мусора и высвобождения ресурсов.
Что делать?
Коллеги из dbtLabs называют это Spider