В предыдущей статье мы поговорили про роль Data Engineer в Х5, какие задачи он решает и с каким технологическим стеком работает. Рассмотрели структуру собеседования, основные направления, по которым мы оцениваем кандидатов, и подробно разобрали базовые требования, предъявляемые нами к уровню владения Python.

В данной статье мы разберём требования к ключевым для Data Engineer в X5 навыкам: распределённые системы и вычисления на Hadoop / Spark, а также SQL и проектирование схемы данных.

Важно отметить, что здесь наши ожидания не ограничиваются базовыми знаниями и умением решать типовые задачи. Мы хотим видеть более глубокий уровень понимания и владения данными технологиями, способность решать сложные или нестандартные задачи.

В продуктовых  командах работать с Hadoop, Spark и реляционными базами данных на базовом уровне умеют многие: Data Scientists, Data Analysts и Data Quality. Для этого прикладываем много усилий по обучению сотрудников: отправляем на обучение к внешним провайдерам, приглашаем внешних экспертов с лекциями, организуем мастер-классы, разрабатываем и преподаем специальные программы в рамках внутренней цифровой академии.

Таким образом, Data Engineer в Х5 — не тот человек, в которого сыплются все задачи, когда нужны данные, а тот, кого зовут для решения более сложных и нетривиальных технических задач, где базовых знаний соответствующих технологий уже не достаточно. По этим причинам мы на интервью смотрим, насколько глубоко кандидат разбирается в том, что происходит под капотом той или иной технологии. Без этих знаний он навряд ли сможет успешно справиться с нашими задачами.

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

Приведу несколько примеров таких вопросов.

Откуда возникают проблемы и как их решать

Ваш коллега, начинающий Data Analyst, собирается построить новую витрину и попросил вас посмотреть код его скрипта на Spark. В коде есть фрагмент, где предполагается провести партиционирование по полю типа double с диапазоном возможных значений от нуля до миллиарда. Что вы на это скажете?

Здесь мы хотим услышать не краткий ответ вида «Так делать нельзя» или «Норм, давай запустим», а более развёрнутый ответ, в котором можно проследить следующие рассуждения:

  • Партиционирование по этому полю приведет к созданию потенциально огромного числа маленьких файлов в HDFS.

  • Наличие сотен миллионов маленьких файлов может привести к проблемам всего Hadoop кластера.

  • Проблема с кластером Hadoop может возникнуть из-за отказа NameNode.

  • Отказ NameNode может произойти потому, что мета-информация о файловой системе хранится в оперативной памяти NameNode, и её объёма может просто не хватить.

Если далее вам предложили развить тему, например, в направлении того, как можно обеспечить высокую доступность NameNode и избавиться от единой точки отказа, то это означает, что вы отлично справляетесь, и есть смысл затронуть более сложные темы.

К вам снова обратился за помощью ваш коллега, начинающий Data Analyst. В этот раз у него проблема с тем, что разработанный им алгоритм прекрасно работает на семплированных данных из большого датасета, но на всём огромном датасете возникает множество проблем. Какие возможные типовые проблемы вам приходят в голову?

Это очень общий вопрос, в ходе обсуждения которого мы бы хотели, чтобы кандидат, среди прочего, затронул тему дисбаланса в данных и то, как с ним справиться. Тема дисбаланса даёт отличный контекст для разговора про поиск узких мест, оптимизацию вычислений, про разные виды join в Spark, чем они отличаются, когда применяются и тому подобное.

Совет: вспомните и расскажите про самые сложные, на ваш взгляд, проблемы и ошибки, с которыми вы сталкивались, как их решили и чему научились. Если про проблему маленьких файлов в hadoop вы знаете не в теории, а потому что в своё время завалили кластер, а потом досконально разобрались в том, почему так делать не стоит, — это даст вам дополнительные очки в наших глазах.

Понимание того, как технология работает под капотом

Вам нужно посчитать количество уникальных значений в неком числовом поле, которое имеет тип Long (8 байт). Какой запрос вы для этого напишете? Расскажите, как Spark будет выполнять этот запрос, настолько детализировано, насколько сможете.

Данный вопрос, при всей своей кажущейся простоте, имеет несколько вложенных уровней сложности и позволяет достаточно точно определить позицию кандидата по шкале от «может написать запрос» до «имеет знания редкой глубины». Ещё раз напомню, что каждый новый виток усложнений, когда вас просят детальнее рассказать про те или иные аспекты, означает, что вы на верном пути, и пока всё идёт хорошо.

Совет: вспомните кейсы из вашей практики, когда вам приходилось оптимизировать вычисления и вникать в то, как именно исполняет запросы Spark. Мы спросим вас про такие кейсы, и тогда разговор можно будет построить вокруг них, что будет для вас гораздо комфортнее, чем общие вопросы.

SQL и проектирование схемы данных

Обычно мы просим кандидата спроектировать небольшую схему и написать SQL-запрос для решения определённой задачи. Цель специально ставится в очень общей формулировке, и ожидается, что кандидат прежде всего начнет с уточнения требований и ограничений, прежде чем предлагать решение или писать какой-либо код.

Вас просят спроектировать схему данных и написать запросы для формирования дашборда по РТО (розничный товарооборот) и маржинальности магазинов новой торговой сети «Десяточка» по месяцам в разрезе городов и категорий товаров.

Здесь мы ожидаем, что кандидат сначала будет уточнять постановку задачи, например, так:

  • Сколько товаров, магазинов и категорий? — 100К товаров, 20К магазинов, 300 категорий.

  • Сколько продаж в месяц?  — Пара миллиардов записей каждый месяц.

  • За какой период храним данные? — За несколько лет, накапливаем новые данные по продажам.

  • Как часто обновляются данные? — Раз в месяц.

  • Какого рода запросы предполагается обрабатывать? — Сценарии использования могут быть разными: к примеру, посмотреть, как изменялись помесячно товарооборот и маржинальность по определенным категориям за последние три года или посмотреть топ-10 городов по товарообороту для всех категорий за последний месяц.

Совет: если вы затрудняетесь предложить решение, которое удовлетворяет всем заданным условиям, выберите ту часть требований, для которых вы можете предложить решение и сосредоточьтесь на них. Явно обозначьте, каким ограничениям ваш вариант удовлетворяет, а каким нет — это совершенно нормальный вариант развития событий.

Одно из возможных решений, которое можно предложить, опираясь на полученные вводные, приводим ниже.

Общий подход к решению

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

  1. Храним сырую историю продаж в Hive.

  2. Сырые продажи можно агрегировать в нужном нам виде (месяц, город, категория) и загружать в более быструю базу для аналитики (OLAP). В качестве такой базы можно использовать ClickHouse, он способен быстро давать результат по аналитическим запросам.

Схема данных

Подход к решению предложен, теперь набросаем возможную структуру схемы для хранения данных. Начнём с Hive.

sales:
   sku_code: int
   store_code: int
   selling_price: float
   cost_price: float
   sale_date: date

stores:
  store_code: int
  store_city: string

skus:
  sku_code: int
  sku_category: string

Теперь перейдём к ClickHouse.

sales_by_city_category:
    sku_category: String
    month: Date
    store_city: String
    rto: Float64
    margin: Float64

Запросы

Запрос для первичной загрузки данных в Clickhouse.

select sum(selling_price - cost_price) as margin
       sum(selling_price) as rto,
       store_city,
       sku_category,
       month(sale_date)
from sales join stores on sales.store_code = stores.store_code
join skus on sales.sku_code = skus.sku_code
group by store_city, sku_category, month(sale_date)

Запрос для загрузки новых данных за очередной месяц.

select sum(selling_price - cost_price) as margin
       sum(selling_price) as rto,
       store_city,
       sku_category,
       month(sale_date)
from sales join stores on sales.store_code = stores.store_code
join skus on sales.sku_code = skus.sku_code
where month(sales.sale_date) = month(from_unixtime(unix_timestamp()))
group by store_city, sku_category, month(sale_date)

Пример запроса для дашборда: сумма всех категорий по городам за текущий месяц.

select sum(rto) as rto,
       sum(margin) as margin,
       store_city
from sales_by_city_category
where month = date_trunc('month', now())
group by store_city

Пример запроса для дашборда: сумма всех городов по категориям за текущий месяц.

select sum(rto) as rto,
       sum(margin) as margin,
       sku_category
from sales_by_city_category
where month = date_trunc('month', now())
group by sku_category

Что ещё спрашиваем на интервью?

Если у вас есть опыт с Docker, K8S, Airflow, NoSQL, Kafka или Gitlab Ci/CD, то нам было бы интересно его обсудить. Типовых вопросов тут как правило нет, мы отталкиваемся от того, что про свой опыт рассказывает кандидат и точечно проясняем интересующие нас моменты.

Надеемся, что эта информация позволит вам составить верное представление о том, как проходит интервью, чего стоит ожидать и как лучше подготовиться.

Мы ищем Data Engeneer со знанием Python, Hadoop, Spark. Чтобы узнать подробности, можно посмотреть нашу вакансию.

Комментарии (1)


  1. dimoobraznii
    25.10.2021 03:19

    Отличный способ привлечь специалиста - подробно рассказать про вакансию и культуру дата инжиниринга. Но, к сожалению, блоги компания часто непопулярны.

    Из статьи понятно какой стек и какие ожидание, дает возможность кандидату подготовиться.

    Лучший вариант искать инженеров данных в РФ это профильном телеграмм канале, в котором уже больше 10т подписчиков - Инжиниринг Данных.