Русский
Русский
English
Статистика
Реклама

Dwh

Как мы внедрили свою модель хранения данных highly Normalized hybrid Model. Доклад Яндекса

26.05.2021 12:10:08 | Автор: admin
Общепринятый и проверенный временем подход к построению Data Warehouse (DWH) это схема Звезда или Снежинка. Такой подход каноничен, фундаментален, вотрфоллен и совсем не отвечает той гибкости, к которой призывает Agile. Чтобы сделать структуру DWH гибкой, существуют современные подходы к проектированию: Data Vault и Anchor modeling похожие и разные одновременно. Задавшись вопросом, какую из двух методологий выбрать, мы в Яндекс Go пришли к неожиданному ответу: выбирать надо не между подходами, а лучшее из двух подходов.

Темы доклада, который вместе со мной прочитал Николай Гребенщиков:
DV и AM: в чем разница и где точки соприкосновения
Гибридный подход к построению хранилища
Сильные и слабые стороны этого подхода
Примеры кода
Дальнейший вектор развития hNhM

Меня зовут Евгений Ермаков, я руководитель Data Warehouse в Яндекс Go.

Я расскажу историю о том, как два руководителя объединились и сделали нечто крутое как минимум, по мнению этих двух руководителей. Расскажу про наш подход к хранению данных в детальном слое. Мы его называем highly Normalized hybrid Model. Надеюсь, что корректно произнес по-английски, я тренировался.

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



Рассказ у нас будет достаточно долгий, из двух глав. Первая глава будет моя я расскажу, как мы вообще к этому пришли. Она, наверное, будет для тех, кто сведущ в этой теме, или послужит повторением того, что вы уже знаете.

Также я расскажу про архитектуру хранилища Яндекс Go в целом, вместе с детальным слоем, где как раз эта модель и применяется. Потом сравню Data Vault и якорное моделирование так, как мы у себя их сравнивали, и объясню, почему мы из этого сравнения сделали вывод, что нужно создавать нечто свое. И расскажу базисные основы про hNhM.



А во второй главе я передам слово Коле.

Николай Гребенщиков:
Я расскажу о нашем фреймворке, который позволяет нам описывать сущности и загружать данные. Покажу, как мы с ним работаем, как загружаем используем и строим витрины над нашим детальным слоем.

В конце мы подведем итоги, самое главное, что можно из этого доклада почерпнуть. посмотрим, куда мы пришли, что сделали и как это можно переиспользовать.



Итак, архитектура Data Warehouse (DWH) в Яндекс Go. Расскажу об архитектуре слоев данных, какая она у нас, какие инструменты хранения и обработки информации есть, и покажу место детального слоя во всей этой архитектуре.



По моему мнению, архитектура слоев данных в нашем хранилище максимально классическая. Мы шли от глаголов действий над данными, которые происходят с хранилищами. Что мы делаем с данными?

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

Потом мы предоставляем эти данные для анализа, непосредственно анализируем и все. Классика.

Отсюда родились наши слои на логическом уровне сверху. Первый слой RAW. Здесь мы сохраняем информацию с источников. Важно, что мы собираем данные как есть. Здесь мы не преобразовываем именно содержимое, но при этом преобразовываем информацию в устойчивый к изменению формат.



Дальше мы все это стандартизируем в операционном слое. Здесь мы храним операционные данные из источника без истории. Возможно, с какой-то ротацией, например год или месяц, зависит от источника и его объема.

Задача этого слоя сформировать набор сущностей источника и предоставить стандартный интерфейс доступа к этим данным, вне зависимости от особенностей источника. Все примерно в стандартном виде.

Дальше детальный слой. Ядро хранилища. Здесь мы храним детальную историю изменений и консолидируем данные между всеми источниками.



На базе этого детального слоя есть слой витрин Common Data Marts.



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



В слое отчетов мы храним отчетные срезы в контексте без потребителя, агрегаты, нечто более понятное для потребителя.



В итоге классика, все по слоям, от RAW до REP. Данные протекают в хранилище, все как завещали Кимбалл и Инмон в своих подходах.

Если это рассматривать прямо по системам, то для того, чтобы захватывать данные, у нас есть сервис репликации данных. Он забирает инкременты и снепшоты из источников самых разных типов, которые есть в Яндексе. И преобразовывает эти данные к устойчивому формату.



RAW, ODS это такой Data Lake. Здесь у нас полуструктурированные данные, каркас MapReduce и всевозможные внутренние аналоги экосистемы Hadoop.

Центр хранилища это непосредственно Data Warehouse. Здесь у нас слои ODS, DDS, CDM.



Основные цели давать ответ на всевозможные ad-hoc-запросы наших аналитиков, выдерживать большое количество Join и достаточно малое время отклика на всевозможные вопросы.

И витрины. Помимо того, что они служат Data Warehouse, мы еще отгружаем их содержимое в системы анализа и визуализации данных. Это кубы данных, отчеты, дашборды, Tableau.



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

То есть RAW, ODS и части CDM-слоя на супербольших данных это у нас Data Lake.



Маленькая ремарка: в Яндексе много внутренних инструментов, которые мы делаем сами. У нас есть своя собственная платформа, есть такие внутренние инструменты, как YT. Можно проводить аналоги: YT это как Hadoop. И в принципе, есть всевозможные аналоги Hadoop-стека.

На Greenplum у нас находятся часть слоя ODS, детальный слой и витрины. Построенные в Greenplum витрины мы затем отгружаем в MS SSAS или ClickHouse для ряда пользователей. Некоторым удобно пользоваться кубами данных, некоторым широкимb плоскими таблицами, и ClickHouse здесь прямо идеален.

Часть витрин доступно для биосистем, или мы делаем из них агрегаты, доступные для нашего BI. BI у нас это Tableau.

Ремарка: я прямо не рассказываю про наш инструментарий, не рассказываю про те возможности платформы, которые у нас есть. Очень советую посмотреть доклад Владимира Верстова, это руководитель разработки нашей платформы, где он подробно расскажет, зачем наша платформа управления данными создавалась, какие задачи она решает и что вообще делает.

Смотреть доклад Владимира

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

Если посмотреть на детальный слой, он не зря находится в самом центре хранилища, потому что это ядро, сердце, ключевое для построения всей доменной модели хранилища.



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

Два важных пункта. Детальный слой должен быть устойчив к изменению в бизнесе. Это достаточно сложно: допустим, мы изменили кардинальные связи или добавили новые сущности. Или у нас полностью изменился подход к бизнесу.

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

Когда мы начали проектировать детальный слой, то провели анализ а какие подходы к построении детального слоя вообще есть? Первое, что приходит на ум, это когда нет никакого детального слоя или подхода.

Отсутствие детального слоя, когда витрина строится прямо по ODS, это нормально по Кимбаллу. Это нормально, если у вас немного источников, если вы небольшой стартап. Но при росте количества источников взаимосвязи между ними и построением витрин становятся настолько большими, что приходится выделять детальный слой, чтобы унифицировать процесс построения витрин.

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

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



Пойдем дальше. Следующий подход классическая звезда и снежинка. Нормализация до третьей нормальной формы, то, что описывал у себя Билл Инмон. Это можно использовать с минимальной подготовкой нужно понимать, что такое первичный ключ, внешний ключ, join-таблички, SCD2, если мы говорим про измерения. И понимать, какие SCD вообще существуют.

Это может быть неудобно перестраивать при изменении кардинальности. Например, есть клиент и заказ. У одного клиента может быть много заказов, но у одного заказа только один клиент. Бизнес поменялся, у одного заказа теперь может быть несколько клиентов, и клиенты могут делить между собой заказ, как это, например, реализовано в такси. Перестраивать все это крайне больно.

При этом минимальное дублирование информации, если мы используем SCD. И какое-то приемлемое количество join. Если аналитики работают с SQL, они должны уметь работать и с этим.

Следующие походы Data Vault и Anchor modeling. Почему я их вывел одновременно? Потому что они предлагают, на самом деле, нечто очень похожее. Это достаточно строгая нормализация, их сложно использовать без подготовки, без понимания, какие таблицы и какие правила они накладывают.



Обе методологии обещают, что их не надо перестраивать. Для Data Vault это работает с ограничениями, я дальше проговорю, какими. Здесь ультрабольшое количество join. При этом обе методологии относительно современны и обещают гибкость.

Посмотрим на эксплуатацию. Все, что справа, Data Vault и якорное моделирование достаточно сложно эксплуатировать из-за большого количества join. Чем больше join, тем сложнее писать SQL-запросы и в целом получать отсюда информацию. При этом проще вносить изменения в модель. Во всяком случае, обе методологии это обещают.

И наоборот, если у нас никакого подхода нет и мы всё вкладываем в плоскую табличку или нормализуем, то эксплуатировать проще. join или вообще нет, или их приемлемо малое количество. Но при этом вносить изменения, перестраивать сложно.

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



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

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

Сперва стоит спрогнозировать требования, а потом подумать, согласовать, подумать еще. Я уверен, что все, кто работал с классическими DWH, которые построены не по Data Vault или по якорю, понимают, что DWH это не быстро.

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

И вообще, у нас Agile, мы гибкие, давайте все переделывать максимально быстро. Желательно, завтра. Примерно так выглядит классический дэвэхашник, когда попадает в Agile-среду, в которой невозможно использовать Waterfall.

Логичные вопросы: откуда родились методологии Data Vault и якорь; может ли DWH быть agile; можно ли подходить к разработке хранилища гибко?

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



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



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

Data Vault вводит и регламентирует несколько основных тип таблиц. Ключевые это Hub, Link и Satellite, дальше я буду о них говорить. Hub хранит сущности, Link обеспечивает связь между хабами, Satellite предоставляет атрибуты и описания Data Vault.

Data Vault 2.0 я не буду касаться. Есть еще специальные таблицы типа bridge и point-in-time. Они упрощают или соединение данных через несколько связей, или получение информации из сателлитов с разной частотой обновления. Это скорее расширяющие, упрощающие модель сущности. Ключевые таблицы это все-таки Hub, Link и Satellite.

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



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

Satellite или сателлит это описательная информация хаба, обычно с историзмом по SCD2. Здесь есть информация о ключе связи или хаба, в зависимости от описательных характеристик того, что находится в этом сателлите, сама информация и технические записи: SCD2, временная отметка даты загрузки, код источника данных.

Если на это посмотреть с точки зрения третьей нормальной формы, то есть вот такая неплохая картинка из презентации самого автора Data Vault:


Ссылка со слайда

Посмотрим на третью нормальную форму. Видно, что бизнес-ключи мигрируют в хабы. Все внешние ключи мигрируют и фактически являются связями. А все описательное, все наши поля, которые несут какой-то смысл, уходят в сателлиты. Сателлитов может быть несколько. Нет явных правил, как правильно разбить атрибуты на сателлит. Есть разные подходы: можно бить по частоте изменений, по источнику, по чтению это такой творческий момент в Data Vault.



Если мы посмотрим на якорное моделирование, то я бы его сформулировал как такую крайнюю форму Data Vault, когда у нас правила еще строже и нормализация еще выше.

У этой методологии есть четыре основных типа таблиц якорь, атрибут, связь и узел. Про узел я не буду подробно рассказывать, фактически это словарь ключ-значение. А про якорь, атрибут и связь сейчас коротко расскажу.

Якорь фактический аналог хаба с важным отличием: здесь нет бизнес-ключей, есть только суррогатный ключ и временная отметка даты загрузки, техническая информация. Все бизнес-ключи фактически являются атрибутами и хранятся в таблицах атрибутов.



Tie связь многих ко многим, один в один как в Data Vault. Внешние таблицы это принципиальный момент, даже если кардинальность связи другая. Но в якорном моделировании есть отличие у связи принципиально не может быть атрибутов.

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

Но расплачиваться за это приходится дорого. Даже очень простая схема, которая загружена на слайде, превращается в гигантское, просто неимоверное количество таблиц.

Скриншот взят с самого сайта якорного моделирования. Понятно, что это не само физическое представление таблиц. Все таблицы, которые с двойным кружком, историзируемые. Все остальное это якоря и связи.



Если мы рассмотрим нашу таблицу в третьей нормальной форме, то суррогатные ключи находятся в якорях. Это не тот суррогатный ключ, который нам приходит, а которые мы сгенерировали в хранилище.



Внешние ключи находятся в наших связях.


Ссылка со слайдов

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

Возникает вопрос: нельзя ли хоть как-нибудь прохачить систему и навесить атрибут на связь? Можно через словарь. Но это именно что словарик, некий типизатор связи. Ничего более сложного на связь в якорной модели навесить нельзя.

Посмотрим на стандартный тест TPC-H. Я уверен, что многие из вас знают, что это такое, но кратко напомню: это стандартный тест для проверки аналитических хранилищ, внизу на слайде есть ссылка на одну из его версий.


Ссылка со слайда

Сейчас на слайде схема в третьей нормальной форме. Здесь есть таблица фактов, таблица измерений, все это связано между собой. Ключи не помечены, но их можно прочитать.

Если мы эту схему преобразуем в Data Vault, то видно, насколько больше таблиц становится, появляются отдельные хабы, отдельные Link. Причем Link сделаны в виде таблиц многие-ко-многим.


Ссылка со слайда

У нас вешаются сателлиты как на Link, так и на отдельные хабы. И в целом, таблица становится больше.

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



Честно говоря, на этом слайде есть небольшое лукавство в том смысле, что я не нашел якорной модели в TPC-H, хотя пытался найти. Взял просто абстрактную схему с сайта якорного моделирования. Но, тем не менее, таблиц должно быть примерно столько, если не больше.

В чем же схожести и различия, если их в лоб сравнивать?



И в Data Vault, и в якоре создается специальная таблица на сущность. В Data Vault это Hub, в якоре это якорь. Разница в том, что в хабе есть бизнес-ключ, а в якоре бизнес-ключ это атрибут.

В Data Vault атрибуты группируются в таблицы-сателлиты. В якоре все строже: один атрибут одна таблица, шестая нормальная форма, все раскладываем на отдельные кубики-элементы.

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

И есть специальные таблицы, в Data Vault point in time и bridge, в якоре knot.

Когда мы вот так, в лоб, их сравнили, ощущение было что выбрать? В чем-то лучше якорь, в чем-то Data Vault. У каждого достаточно цельная методология. Якорь построже, но, с другой стороны, возникает меньше вопросов и больше пространства для автоматизации.


В общем, как-то так. Яндекс славится тем, что создает свои инструменты. Почему бы нам не создать свою модель?

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



Из этой идеи и родилась наша гибридная модель highly Normalized hybrid Model, hNhM. Здесь я кратко расскажу ключевые идеи модели. (...)



Ключевая идея выбирать оптимальный формат хранения. Мы не ограничиваем себя ни Data Vault, ни строгостью якоря. Но при этом позволяем выбрать либо одно, либо другое. А если мы позволяем это выбрать, то с точки зрения методологии классно было бы разделить логическое и физическое моделирование.

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

И удобство построения витрин. Потому что да, таблиц очень много, есть большой риск написать не оптимально. Это даже не то чтобы риск. Просто вокруг тебя раскиданы мины. Очень хочется дать инструментарий, чтобы со всем этим было удобно работать.

Если пойти по этим идеям, посмотреть очень верхнеуровнево, то мы это представляли так.



У нас есть слои, про которые я рассказывал, RAW. ODS это наш Data Lake. В RAW мы захватили данные, они лежат как есть. В ODS мы их чуть почистили, но это операционные данные, без истории. В детальном слое мы фактически разложили это все на маленькие кубики-сущности. С точки зрения логического проектирования это сущности-связи между ними. С точки зрения физического хранения на нашем Greenplum это скрыто с точки зрения использования.

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

То есть мы сначала разбираем все на кубики, а потом из этих кубиков собираем что-то красивое. Во всяком случае, мы себе так это рисовали на старте. К концу доклада вы сможете понять, получилось у нас сделать это так, как мы это изначально планировали, или нет.

Про разделение логического и физического уровня. Мы хотели на старте их разделить явным образом.

Логический уровень это всем известные ER-диаграммы. Уверен, что все, кто работает с данными, про ER-диаграммы знает. Здесь есть сущности и их ключи, связи между сущности и атрибутивный состав сущностей. Мы еще добавили необходимость их историзации, но это как дополнительная информация к атрибутам.

Логический уровень когнитивно сложная часть проектирования. Он не зависит от СУБД в общем виде и достаточно сложен. Трудно выделить сущности и домены в бизнесе, особенно если ты его не знаешь так, как основатели этого бизнеса.



Физический уровень это скрипты DDL. Здесь выполняется партицирование сущностей, объединение атрибутов в группы, дистрибьюция в системах MPP. И индексы для ускорения запросов. Все перечисленное мы хотели скрыть. Во-первых, оно зависит от СУБД и технических ограничений, которые у нас есть. Во-вторых, нам хотелось сделать так, чтобы этот физический уровень был невидим, чтобы мы могли переключаться между Data Vault и якорем, если захотим.



Разделяя так логический и физический уровень, мы фактически делим задачи между нашими ролями. У нас есть две роли:

  • Партнер по данным, Data Partner. Мы так переименовали Data steward. Да, слово steward имеет здравое значение в переводе: распорядитель чего-то чужого, а данные в этом смысле чужие. Но все равно партнер по данным звучит гораздо лучше.

    Партнер по данным на логическом уровне должен ответить на вопрос концептуальной модели: какие вообще направления бизнеса и взаимосвязи между ними у нас есть, как часто будут меняться атрибуты. И из этого построить логическую модель, прямо по классике.
  • И инженер данных, здесь все классически. Он отвечает на вопросы физической модели: как хранить атрибуты, нужны ли партиции, нужно ли закрытие SCD2. И обеспечивает сам расчет данных по инкременту, а также пересчет истории, то есть фактически ETL-процесс.

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



В итоге, базируясь на ER-диаграммах, мы используем сущность и связь. Сущность базовый кубик любого описания домена. Здесь, на логическом уровне, мы хотели сделать так, чтобы сущности описывались наименованием и комментарием; именем сущности и его описанием; бизнес-ключом, который обязателен для любой сущности, чтобы понимать, что его определяет; и каким-то набором атрибутов.

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

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

Атрибут это таблица. Она содержит информацию об одном атрибуте, может содержать историю, а может не содержать. Group это группа атрибутов, как сателлит в Data Vault. Она может содержать информацию о нескольких атрибутах. Важное ограничение на уровне модели: все атрибуты должны приходить в эту группу сателлита из нового источника и иметь один тип историзма.



Связь. На уровне ER-диаграммы понятно, что это такое. С точки зрения логического уровня это список связанных сущностей и кардинальность связи. Здесь мы пошли по жесткой модели якоря и не позволили хранить у связи никаких атрибутов. Если хочется повесить на связь атрибут, то для этого должна быть отдельная сущность.

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

Итого мы получаем примерно такую картинку: На логическом уровне рисуем ER-диаграмму или описываем нашу сущность.



А на физическом уровне получаем вот такой набор таблиц. Видно, что тут есть и группы атрибутов, и просто атрибуты, и связи. Хотелось бы, чтобы все это было скрыто. Причем на обоих уровнях работы с DDS: и на загрузке данных, и на построении витрин.



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

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

На уровне построения витрин скрываем сложность групп, связей, хабов. Мы работаем с сущностями на логическом уровне, а они с таблицами на физическом уровне. Это нам позволяет скрыть сложности исторической обработки записей и максимизировать простоту работы с инкрементом.

Всего тут есть три концептуальные идеи:

  • Закрепить разделение логического и физического проектирования в методологии, полностью закрыть физическое проектирование.
  • Сокрытие физического проектирования провести как при загрузке данных, так и при использовании модели, чтобы было удобно загружать и строить витрины.
  • И так как мы все сокрыли с физической точки зрения, то нужно еще выбирать оптимальный формат хранения для каждого конкретного случая: группировать и догруппировать атрибуты. Все это скрыто внутри.

Если возвращаться к Data Vault и якорю, то фактически из Data Vault мы взяли сателлиты и назвали их у себя группой атрибутов. Позволили отойти от этой строгости якоря, где один атрибут одна таблица.

Из якоря мы взяли к ебе сам якорь. То есть мы не храним бизнес-ключи в ключевой таблице, а смотрим на них как на еще одни атрибуты. Связи идут через отдельную таблицу, как в Data Vault или якоре, но мы запретили вешать на них атрибуты. В перспективе хотим разрешить.



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

Из Data Vault мы взяли специальные таблицы point in time и bridge для упрощения своей собственной внутренней работы с hNhM.

И если подводить итог, этим всем категорически невозможно управлять без фреймворка. Если вы попробуете все это воссоздать полуавтоматически или DDL-скриптами, то можно буквально убиться об количество таблиц и весь объем данных, метаданных, которыми надо управлять.



Поэтому мы разработали так называемый hNhM-фреймворк. Здесь я передаю слово Коле. Он подробно расскажет, что мы конкретно разработали и как мы этим пользуемся у себя внутри.

Да, я расскажу о нашем фреймворке что сделали, как храним и как с этим работаем.

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

Смотреть доклад Владимира

В левой части сервис репликации, написанный в Go на базе MongoDB, которая позволяет получать данные из всех возможных источников. Это может быть как реляционная база данных, так и нереляционная. Это может быть API, при этом и мы можем читать из API, и API может к нам пушить данные. То есть перед нами вещь, которая в себе собирает изменения.



В центральной части находится наш Data Lake, который хранится на YT. Это аналог экосистемы Hadoop, в нем мы храним слои RAW и ODS. Сейчас у нас объем данных около двух петабайт.

И само хранилище на Greenplum, в котором находится частично ODS за очень небольшое количество времени и слой DDS. Сейчас Greenplum у нас весит, по-моему, около пол-петабайта. На каждом слое мы умеем вычислять инкремент, что позволяет ускорить загрузку, избавиться от лишней работы по чтению и обработке данных.

Детальный слой основной элемент хранилища на Greenplum. На его основе мы строим все наши витрины.



Отличительная особенность платформы: все сущности на всех слоях описываются в питонячем коде, но это видно и на слайде. Особенности описания сущностей в DDS мы рассмотрим на основе сущностей Person сотрудник.



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

Для чего нам нужна документация? На ее основе во время релиза платформа собирает документацию по всем нашим объектам и автоматически выкладывает на внутренний ресурс, это удобно для пользователей. Потому что им не надо следить за анонсами по изменению структуры. Они могут пойти в документацию, попробовать найти то, что им нужно, и начать пользоваться данными.



Далее необходимо описать технические параметры layout. С помощью трех полей Layer, Group и Name мы определяем путь до места хранения объекта в нашем хранилище. Неважно, будет ли это YT, Greenplum или что-то еще в будущем.



Следующим шагом мы начинаем описывать все атрибуты, которые есть в сущности. Для этого мы используем предопределенные типы, которые также едины во всей платформе. Для каждого атрибута нужно обязательно указать документацию. Я уже говорил, что это сделано для удобства пользователей.

Для каждого атрибута указываем тип историчности.



Сейчас у нас есть три типа. Ignore это когда пришедшее значение записывается, а мнение с большей бизнес-датой игнорируется. Update когда при изменении значение перезаписывается. New исторический атрибут.

Атрибуты с Ignore и Update не хранят историю изменений и отличаются тем, что в Ignore предпочтение отдается значению с меньшей бизнес-датой, а в Update с большей.



Также для каждой сущности мы указываем логический ключ.

На слайде видно, во что физически превращается каждая сущность.





Тут, наверное, сложно сходу что-то понять, поэтому я детально опишу основные особенности каждой таблицы. В каждой таблице есть поле SK, это суррогатный ключ, который мы получаем из натурального ключа. Используем хэш, это удобно, потому что при загрузке любого атрибута, любой сущности, любого хаба, мы можем на основе данных из системы источника сгенерить хэш и иметь внутренний ключ в хранилище.

Также в каждой таблице есть поля, в которых хранятся даты действия атрибута.





Если у данного атрибута историчность New, то это два столбца utc_valid_from_dttm и utc_valid_to_dttm. То есть это метка во времени, с которой определяется действие конкретной записи.

Для атрибутов типа историчности Update и Ignore действует только один столбец: utc_valid_from. Это бизнес-дата, с которой мы узнали, что атрибут имеет текущее значение.





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

В якорной модели каждый атрибут должен храниться в отдельной таблице, но в реальной жизни это оказалось не очень удобно для таблиц фактов.



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

Поэтому мы решили, что круто уметь атрибуты объединять в группы, чтобы их хранить в одном месте. Это позволит и ускорить загрузку и упростить чтение данных. Для этого мы добавили свойства групп их надо задавать у атрибутов, которые должны быть объединены в группу.

В группы можно объединять только атрибуты с одинаковой историчностью. И важно понимать минус этого решения: атрибуты должны приходить все вместе, иначе мы будем терять информацию. И желательно, с одинаковой частотой изменения. Потому что если один атрибут имеет гораздо большую частоту изменений, то при создании исторических данных все атрибуты будут лишний раз дублироваться.

Структура после объединения части атрибутов в группы выглядит уже гораздо более компактно.





Например, все флаги мы объединили в группу flg, общую информацию в группу info, ключевые атрибуты в группу key.

Теперь поговорим про объявление связей. Каждая связь тоже описывается в отдельном классе.



Атрибутами связи являются сами сущности, поэтому атрибуты Link должны быть объединены с объектами специального класса. Также обязательно для Link указывать ключ. Ключ определяет набор сущностей, которые однозначно идентифицируют строку в Link.

Каждый Link по умолчанию является историческим. Таким образом, в нем всегда есть два поля: utc_valid_from и utc_valid_to.

В этом примере мы делаем Link между департаментом и сотрудником. В данном случае ключом Link является сотрудник.



На слайде мы видим, как это физически реализовано.



Видно, что у Link нет суррогатного ключа, потому что его суррогатным ключом являются все те сущности, которые входят в ключ Link.

Теперь мы обсудим, как загружаем сущности.

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



В самом простом случае этот кубик выглядит так.



В самом начале мы описываем источник. Это stage-таблица, для которой есть точно такой же класс, написанный отдельно в платформе.

Также мы указываем поле, в котором хранится бизнес-дата и система-источник.

Далее мы делаем маппинг между атрибутами. Описание таргета состоит из двух частей указание сущности, куда мы загружаем атрибуты, и маппинг самих атрибутов.



Как мы видим, в качестве таргета указана таблица Person. Дальше следует маппинг атрибутов. Это самый простой вариант описания. В реальной жизни все выглядит гораздо сложнее. Например, так.



Мы видим, что из одной stage-таблицы данные грузятся в несколько сущностей. Одна сущность может грузиться несколько раз. Это в данном случае e-mail, он может быть в stage-таблице и персональным, и рабочим.

В большинстве случаев при загрузке Link не обязательно указывать маппинг, так как на основе этой информации мы можем понять, какие сущности указаны в Link, и найти маппинг этих сущностей в таргетах.

А если нет, то, скорее всего, это ошибка и мы об этом сообщаем пользователю.



Но в ряде случаев мы не можем автоматически этого сделать. Например, в stage-таблицу приходит и персональный почтовый ящик, и рабочий. Но и в обоих Link используется одна и та же сущность e-mail. Мы их специально не разделяем, потому что в реальном мире e-mail единая сущность. В то же время нам надо понять, какое здесь поле, какой Link грузить. Мы добавили специальный параметр, который позволяет определить, какое поле куда идет.

Дальше есть вот такой граф загрузки. Для каждой такой загрузки, для каждого такого лоудера генерится свой граф загрузки, который состоит из атомарных загрузок атрибутов Hub Link. Суррогатный ключ генерится как хэш, поэтому он может создаваться на каждом этапе самостоятельно.



Все задачи внутри графа выполняются параллельно, в зависимости от типа изменений. Это либо Insert\Update по SCD2, либо Insert. Данные из разных источников также могут загружаться параллельно.



Дальше я расскажу, как мы используем наш hNhM.

На основе DDS мы строим витрины. Пользователи напрямую могут обращаться к нашим сущностям и просматривать данные.



У нас есть два основных типа потребления данных DDS: ad-hoc-запросы от бизнес-пользователей и построение витрин. ad-hoc-запрос реализован двумя способами либо view, либо через функции. И то и то скрывает реализацию от пользователей.

Витрины мы строим с помощью нашего фреймворка и тоже полностью скрываем реализацию.

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

После изменения описания сущности все витрины, которые использует тот или иной атрибут, теоретически могут развалиться, потому что атрибут был исторический, стал не исторический, и это изменит структуру таблицы.

В конце концов, это просто очень сложно написать что-то с select, где будет 20-30 join.

Как у нас происходит доступ к сущностям из Python?



Было разработано несколько классов, которые позволяют сформировать SQL-запрос к определенной сущности и затем использовать его в построении витрины. Как это выглядит?



Мы описываем CTE. Оно может быть либо историческое, либо актуальное.



В первую очередь мы указываем сущность, которую хотим использовать.



После этого указываем необходимые атрибуты, которые мы будем использовать для витрины.



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

А так выглядит SQL-запрос для построения витрины. Есть переменные постановки, которые полностью скрывают реализацию. Потом идет сам запрос, который их использует. При этом можно использовать переменные как в визе, так и во временных таблицах. Это уже зависит от объема данных.



Как происходит доступ к сущностям из СУБД?



У нас есть вьюхи и есть две специальные процедуры. Первая get_entity. Она получает в качестве параметров сущность, столбцы и создает временную таблицу, в которой будут, собственно, все необходимые данные процедуры.

И вторая процедура это когда мы от entity к существующей таблице через Link добавляем дополнительную сущность и расширяем нашу табличку. Теоретически ключ у нее даже может поменяться.

А сейчас чуть более подробно покажу, как это работает.



Мы вызываем специальную функцию.



Указываем сущность.



Указываем столбцы, которые необходимо получить. Получить все столбцы сейчас нельзя, потому что для больших сущностей достаточно дорого считать всё вместе. Поэтому мы заставляем пользователя указать конкретные атрибуты, которые он хочет получить.



Дальше указываем временную таблицу, в которую надо положить данные.

Затем выполняется эта процедура. В ней создается таблица, куда вставляются данные. После этого можно просто селектить таблицу и смотреть подготовленные данные.



Примерно так же выглядит добавление сущности к уже созданной таблице.



Мы указываем таблицу, в которой уже есть сущность.



Указываем Link, через который мы хотим подключить дополнительную сущность.



Указываем саму сущность, которую мы хотим добавить.



И столбцы этой новой сущности. При этом мы можем как положить данные в уже существующую таблицу, так и создать новую.



Дальше мы расскажем, какие задачи оптимизации мы решали. Тут я передам слово Жене.

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

Посмотрим на объявление класса. Например, на объявление сущности Person.



В физическом мире мы можем представить ее в виде один атрибут одна табличка.





Это будет вполне нормально с точки зрения якорной модели. А можем ли мы сгруппировать атрибут? Здесь часть сгруппирована: флаги отдельно, информация о персоне отдельно, ключи отдельно, атрибуты отдельно.





Какая схема лучше? С точки зрения загрузки данных все то же самое. С точки зрения чтения данных все это скрыто, Коля про это рассказал. И с точки зрения использования будет одинаково.Не можем ли мы сделать так, чтобы эта физическая модель выбиралась наилучшим, оптимальным образом?



Мы постарались это свести к оптимизационной задаче. У нас буквально стояла проблема с объемом данных на Greenplum. Сейчас она не стоит. У нас действительно полупетабайтовый кластер на Greenplum, это достаточно много. Но еще буквально год-полтора назад даже такого не было.

Перед нами стоял вопрос: как оптимально объединить атрибуты по группам так, чтобы минимизировать объем хранения информации. У нас были метаданные объектов, это все наше описание того, что за объекты у нас есть. Маппинги полей и загрузчиков, количество строк в объекте и инкремент, а также накопленное знание о частоте изменений. У нас свой metaDWH, в который мы складываем всю эту информацию, и могли бы ее переиспользовать.

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

Будем минимизировать занимаемое место на диске. Вот такую задачу мы себе поставили. Как мы ее решали?



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

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

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



Из них следующие, из них следующие так мы получаем пространство всех возможных состояний, в которые мы можем этими атомарными операциями попасть.

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

Берем лучшие, скрещиваем их между собой, проводим новые мутации и так далее, классический генетический алгоритм.

В итоге при признаках какой-то сходимости мы останавливаемся, получаем итоговое состояние, которое лучше текущего по нашей оптимизационной функции. Сравниваем метаданные между собой и генерируем скрипт миграции. Как проходит миграция, как аккуратненько смигрировать так, чтобы пользователь не заметил, что у него физически что-то поменялось, это отдельный большой вопрос на целый доклад, мы специально его не освещаем.

Я делаю акцент на том, что оптимизация была по месту, потому что применимость этого подхода гораздо шире. Гораздо интереснее оптимизировать не место, которое мы фактически сейчас закупили, это не для нас не проблема, а скорость чтения и скорость вставки информации. Усложнить нашу матмодель так, чтобы мы оценивали не только по месту, но и как-нибудь более сложно.

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



К чему мы пришли, зачем мы сюда пришли и стоит ли вообще повторять наш путь?

Первый, на наш взгляд, инсайт от доклада сравнение Data Vault и якоря. Мы поняли, что нам нужен детальный слой, что есть современные методологии Data Vault и якорь, и провели между ними сравнение. Они очень похожи. Я воспринимаю якорь как более жесткую форму Data Vault: в последнем есть правила, а в якоре эти правила еще более сложные.



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


Наш вариант это гибридная модель, мы ее назвали hNhM. Требования я не буду перечислять, они на слайде. Основных идей три. Логическое и физическое проектирование, физический мир полностью скрыть как с точки зрения загрузки информации, так и с точки зрения ее получения.

Раз мы все это скрыли, то можем для физического мира выбирать нечто наиболее оптимальное. Оптимизировали именно хранение информации, но можно сделать нечто более интересное, например чтение.

На слайде показано, что мы взяли из Data Vault, а что из якоря.

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



Всем этим невозможно управлять без фреймворка. На явный вопрос, сколько у нас это заняло времени, Коля ответил квартал. Но именно потому, что мы базировались на существующем внутреннем решении. На схеме было видно, что мы лишь маленький кубик в нашей общей платформе. Однако квартал это не совсем честно. У нас был еще квартал на проектирование, размышление, не то чтобы это абсолютно чистая разработка.

Этот путь мы не советуем повторять. Если у вас нет свободной горы разработчиков, которые настолько круто прокачаны в Python, что готовы все это повторить и аккуратно сделать так же, как у нас, лучше не вступать на этот скользкий путь. Вы просто убьете очень много времени и не получите того бизнес-результата, который можно получить, используя Data Vault, якорь или гибридную модель, как мы.



Что же делать? У нас есть большой roadmap развития. Можно добавить больше гибкости например, я говорил, что на связь мы не вешаем ни атрибуты, ни группы. Но здесь можно это реализовать и воссоздать все типы таблиц Data Vault и якоря.

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

DSL у нас хоть и есть, но хотелось бы полноценный язык, который позволит строить витрины с учетом инкремента так, чтобы была полная кодогенерация. Пока она частичная.

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

Что делать, если у вас нет разработчиков? Дождаться, когда мы это заопенсорсим. У нас есть такие мечты. Вполне возможно, что мы свои фантазии осуществим и заопенсорсим этот продукт.

Следите за обновлениями или, если не хотите следить, приходите к нам и создавайте гибридную модель вместе с нами. На этом все. Спасибо за внимание.
Подробнее..

Business Intelligence на очень больших данных опыт Yota

16.02.2021 16:13:42 | Автор: admin


Всем привет! Меня зовут Михаил Волошин, и я, как руководитель отдела инструментов бизнес-анализа, хочу верхнеуровнево рассказать о плюсах и особенностях BI-решения Yota.

200 Tb Vertica, 400 Tb Hadoop, кластер Tableau, специфичная организация процесса разработки и многое другое ждут вас под катом.

Внимательный читатель спросит: А причем тут Vertica и слоник Hadoop, технологии же разные? Да ни при чем это лишь КДПВ.

1. DWH: ода Вертике


Vertica. На ее базе построено корпоративное хранилище данных (data warehouse, DWH), являющееся ядром решения. Наша Vertica первая инсталляция в СНГ была развернута в 2012 году (я пришел лишь в 2016). 8 лет назад не было и половины зоопарка продуктов Apache, а выбор происходил между Netezza, Greenplum и, собственно, Vertica. Время показало, что выбор оказался верным: IBM прекратила техническую поддержку Netezza в 2019, Greenplum еще в 2015 стал opensource продуктом (т.к. никто не покупал шардированный Postgress). И к началу 2021 года в мире осталось 2 серьезных аналитических on-premise БД: Vertica и Teradata. Не хочу разводить холивар, но буду рад услышать об иных решениях, позволяющих обычным аналитикам в adhoc запросах оперировать >1 трлн строк за разумное время в минутах и без поддержки команды data engineer + dataops.

Итак, Vertica это колоночная MPP БД. Т.е. данные хранятся в колонках, что ускоряет доступ к ним и позволяет оптимизировать хранение. Запросы выполняются одновременно всеми нодами кластера, что также позитивно сказывается на скорости обработки данных (однако происходит высокая утилизация сети и дисков). При этом входной порог для доступа к терабайтам и петабайтам данных низок за счет ANSI SQL 99 с небольшими расширениями. 1-й Tb этого великолепия бесплатно. Важный момент все колоночные решения не соответствуют ACID, т.е. не могут заменить классических OLTP БД для условного биллинга, но отлично подходят для целей анализа данных. Более подробно об архитектуре Vertica здесь.

У нас 161 Tb на 34 rack нодах HP, каждая из которых имеет:

  • 2*CPU по 20 ядер
  • 256Gb RAM
  • 2*10G сеть
  • быстрые 10k SAS HDD RAID 10 (в 2017/18, когда мы планировали обновление и обновляли RAID массивы, SSD стоили как чугунный мост и были не такими надежными как сейчас)

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

В целом продукт достаточно надежный: за все время, что я работаю в Yota (5-й год пошел), кластер ни разу не падал целиком. Были кейсы, когда 9 нод вываливались в течение 10 минут (диски, контроллеры рейдов, иные технические проблемы), и это приводило к просадкам производительности, но кластер не рассыпался, и после вывода сбоивших нод из кластера на горячую производительность восстанавливалась. Вывод необходим, т.к. кластер всегда работает со скоростью самой медленной ноды (вспоминаем рекомендацию вендора о гомогенности). Теоретически из строя может выйти до половины всех узлов кластера, но может хватить и 2 нод (при k-safety=1, параметр репликации данных со стандартным значением для большинства инсталляций в мире).

Еще одним фактом, касающимся надежности DWH, хотя и не красящим нас, является появление бэкапа: он у нас появился лишь в 2019 перед мажорным обновлением версии Vertica. И это при том, что до 2018 года наша Vertica была самой большой в СНГ (сейчас по объему вторая-третья, но по сложности самого хранилища, по-прежнему, первая).

Обновлялись мы, кстати, сразу на 2 версии (7 -> 8, 8 -> 9). Ну, как обновлялись: в 13:00 остановили кластер и запустили .py скрипт от вендора, а в 21:10 мы уже открывали пиво, после того как кластер начал подниматься. Никаких эксцессов не было. И тут вспомнилась статья на Хабре от коллег из телекома про обновление кластера Greenplum c 4-ой до 5-ой версии. Так они, насколько помню, потратили сотни дней разработчиков на costylmaking из-за несовместимости типов данных между мажорными версиями одного продукта.

Отчасти лукавлю, не рассказывая о сути стабильности нашего кластера, которая кроется в четкой настройке и управлении пулами ресурсов для оркестрации выполняющихся запросов. Это настоящее искусство, лежащее в основе DWH Vertica c uptime из множества девяток после запятой.

Anchor modeling, Datavault 2.0 всего этого у нас нет. Мы не фокусировались на жестком соблюдении какой-то одной изначально выбранной методологии, иначе сами себе устроили бы приключения. Почему? Хотя бы потому, что при разворачивании DWH, Yota была независимой компанией и крупнейшим оператором 4G, но предоставлявшим доступ в сеть только для модемных устройств. После покупки МегаФоном в Yota появились голосовые абоненты, а голос принципиально иной продукт, и мы бы просто не запустились в крайне сжатые сроки, если бы не определенная архитектурная свобода. У нас 37 схем, и архитектура внутри каждой не то, что схемы, но даже витрины, может отличаться от мейнстрима и выбирается в соответствии с решаемой задачей с учетом особенностей хранения в источниках.

И еще момент во внутренней команде нет ни архитектора, ни девопс-гуру. Они просто не нужны fulltime, т.к. Vertica не требует постоянного обслуживания. Эти роли у нас выполняются подрядчиком, а внутренняя команда сфокусирована на создании инструментов анализа бизнес-данных для всей компании и совместном с бизнесом улучшении продуктов. Как бы высокопарно это ни звучало, но Yota изначально data driven business. У нас под сотню персональных учеток для adhoc-запросов и широкого доступа к данным всем, кому он нужен.

В завершение разговора о Vertica хочется обсудить регулярно поднимающийся вопрос: Дорого же! Зачем оно надо?. По моему скромному мнению, в бизнесе нет понятий дорого/дешево, но есть понятие эффективно/не эффективно. Давным-давно я работал в складской логистике, так вот, строительство склада начинается с изучения характеристик будущих единиц хранения (SKU) и потоков движения этих SKU. При проектировании хранилища ситуация должна быть аналогичной: изучение данных, подразумеваемых для обработки внутри DWH, выбор наиболее оптимальной архитектуры с параллельными расчетами финансовой модели. Звучит просто, но это позволит избежать догматов: Делаем только на opensource или Наш потрясающий стартап может себе позволить Teradata в топ-комплектации. Пару месяцев назад создал модель Vertica total cost of ownership, и эффективность текущего решения Yota вышла оптимальной. Поделиться, к сожалению, по понятным причинам не смогу.

Hadoop. Их у нас целых 2 кластера (Cloudera 6.3), которые мы используем как дешевое хранилище некритичных для бизнеса данных. К данным, хранящимся в наших Hadoop, не требуется скорость доcтупа, предъявляемая к Вертике. Здесь стоит отметить подставу со стороны Cloudera: когда мы наши Хадупы планировали и разворачивали в 2018-2019, то существовавшая Comminity Edition нас вполне устраивала; однако в феврале 2020 пришла полярная лисичка в виде изменения политики лицензирования и, по сути, отмены т.н. free версий. Из-за этого вынуждены думать сейчас о редеплое кластера из 23 нод на CH 5.16 с потерей данных (ими можно пожертвовать). А на маленький кластер Hadoop вынуждены оформлять ненужную нам лицензию.

Oracle. Легаси-вишенкой на торте DWH выступает хранилище Oracle объемом всего 1.4 Tb. Его мы иногда используем для собственной обработки в ODS слое высокочастотных потоков малонасыщенных данных. Например, 100 000 файлов в сутки по несколько строк в каждом, конечно, можно писать в Вертику напрямую, но разумнее сначала в транзакционную БД, а уже затем часовыми батчами в DWH. Движемся дальше.

2. ETL


В нашем случае зачастую ELT, так как наше DWH позволяет перемалывать терабайты внутри себя без реализации стадии Transform на относительно слабых ETL-серверах.

Высоконагруженные потоки данных. У нас 9 пайплайнов по 2-8 ETL-джобов в каждом. Они редко меняются, и поскольку границы не выходят за staging слой, то мы отдали их нашим подрядчикам. Тем же, которые поддерживают Vertica. Коллеги написали свой Loader на Groovy 3, который сами и поддерживают. Loader вполне неплохо перемалывает свой 1 Tb в сутки, поступающий в Vertica, и до 10 Tb в большой Hadoop.

Из интересного стоит упомянуть используемый нами механизм CDC от Oracle Oracle Golden Gate. Kafka пока не используем, но, возможно, начнем, т.к. переезд на Oracle 19 имеет специфичную реализацию Oracle for BigData вместо старого доброго OGG. На текущий момент мы еще в процессе исследований, но как бы не пришлось свои костыли писать

Остальные потоки данных. Здесь кроется соль нашего решения формирование промежуточных и конечных витрин как на основе данных из п. 2.1, так и на собственных интеграциях примерно с 150 системами-источниками. Этим занимается исключительно внутренняя команда. Здесь примерно 1150 ETL-джобов. В основе стэка разработки: Talend Data Integration 7.1. Инструмент условно бесплатный. Условно, т.к. требует лицензии для использования среды выполнения и оркестрации. Я уже не застал того благостного времени, когда использовалась Talend Administration Console, но старшие товарищи рассказывали, что это был тот еще садомазочуланчик папаши Мюллера образцовый UI, привносящий незабываемый UX. Можно, конечно, деплоить джобы Talend в виде .zip пакетов сразу в .sh и оркестрировать в cron, а потом грепать логи. Но было решено еще в 2016 году, что деплоить джобы Talend будем сразу в Scheduler (рантайм с web UI для доступа к нему). Который, как уже понятно, написал под нас тот же самый подрядчик. Разумеется, и лицензия стоит дешевле чем TAC, UI оставляет более позитивный UX, и доработки под наши пожелания не затягиваются во времени.

Пара слов про Talend Data Integration. Это среда визуального программирования потоков интеграции. Сам инструмент не уступает Informatica PowerCenter по производительности. JVM под капотом у обоих. Максимум, что придется писать руками SQL для стадии Transform внутри некоторых компонентов, но его и нет смысла пытаться чем-то заменить. Чтобы не было сомнений в возможностях Talend и иных интеграционных комбайнов, 2 факта:

  • до появления Loader сотни Гб бинарников CDR парсились джобами Talend. Loader и появился из доработки джобов Talend, которые перестали справляться с нагрузкой;
  • внутренняя команда иногда переписывает за подрядчиком пайплайны, созданные в их Loader, и время на обработку данных уменьшается. Понятно, что ситуация разовая, и 1 Tb в сутки из бинарников вряд ли Talend распарсит, но факт есть факт.

3. Визуализация данных


Используем следующие инструменты: MS Analysis Services, Tableau, есть у нас и любимое легаси в виде SAP BO.

MS Analysis Services. Исторически аналитические кубы были значимым инструментом. В проде у нас всего 16 кубов весом от 6 Mb до 144 Gb, а через пару месяцев после доработок и до 200 Gb. В 2020 году возникла идея о возможном переносе кубов в Tableau, но там уже при экстракте в 5 Gb дэшборд стал люто тормозить. В нашем случае платформа оказалась безальтернативной. Кстати, используем последний free version MS AS 11. Не PowerBI, конечно, но нулевые траты на лицензии нас вполне устраивают.

Tableau. На конец 2020 у нас было 277 дэшбордов, и бизнесу они адски заходят. Одна из целей 2021 максимальная автоматизация ручной отчетности аналитиков. И тут мы споткнулись, т.к. наши аналитики, как и любые нормальные аналитики, для прототипирования используют Excel. Без шуток.

Есть у этих самых аналитиков любовь к типам диаграмм 'водопад':

Прошу прощения за низкое качество изображения, но суть передана верно

Очень круто выглядит и нравится топ-менеджменту. Как бывший аналитик данных, сам кайфую, когда вижу такую красоту. Но чтобы реализовать такой водопад в Tableau, нужно сделать 5 графиков, обеспечить синхронизацию фильтров между ними Ок, пару накликать можно. А если в дэшборде их 171? Ну, вы поняли. На одной стороне весов 12 человеко-часов аналитиков на ежемесячный сбор презентации. На другой полгода разработки сеньором + 100% гарантия превращения дэшборда в недвижимость. Недавно был тяжелый разговор с аналитиками, где мы зафиксировали, что такой красоты может быть не больше 2-3 графиков на весь дэшборд. Но продолжаем искать пути автоматизации именно этого типа визуализации в юзкейсах наших аналитиков адская идея скриптами powershell повторить ручные действия в Excel (там их пилят при помощи платной надстройки ThinkCell) пока не отпала. Офтопом стоит отметить сам факт повторения многостраничных презентаций в Tableau, где на самом деле однотипные данные намертво распечатаны в .pdf во всех возможных измерениях имеющихся в дэшборде. Конечно же, подход спорный, но мы очень клиентоориентированы по отношению к внутренним заказчикам, и мысли об изменении в сторону сторителлинга аккуратно и потихоньку продвигаем в жизнь.

Sap BO. Очевидная legacy система визуализации устаревшая чуть более, чем полностью. Аккуратно уходим от нее в сторону более современных и гибких решений, т.к. она прекрасна для point-to-point повторения отчетов (именно тут необходимо собирать большие и однотипные презентации аналитиков, но трудозатраты будут еще выше, да и такие водопады вообще не факт, что реализуемы в SAP BO), но не позволяет создавать интерактивные дэшборды. Следует отметить, что сам подход реализации point-to-point больших презентаций актуален для большого российского бизнеса, например, из сферы добычи сырья. В 2к21 это, на мой взгляд, выглядит морально устаревшим, особенно для Yota, средних размеров data driven business. Поэтому нам не имеет смысла заниматься реализацией намертво прибитых по брендбуку отчетов на миллион вкладок/страниц.

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

4. Data science (machine learning)


Кроме классического BI в отделе есть команда DS и, надеюсь, в этом году здесь появится ссылка на статью о Data Science в Yota, написанную профессионалом. Я таковым не являюсь, т.к. вырос из разработчиков классического BI. Извините, если кто-то зашел сюда только ради этого :-)

5. Agile? Нет У нас своё


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

В направлении классического BI 6 инженеров и тимлид. При этом нет ни выделенных архитекторов, ни аналитиков, ни тестировщиков, ни релиз-менеджеров. Каждый инженер = BI-фулстэк, реализует задачу под ключ, напрямую общаясь с бизнес-заказчиком и лично ответственен за конечный результат. Кодеров по ТЗ/токсичных рок-звезд нет и не будет от слова совсем. Но у всей команды изначально хорошие софт-скилы вдобавок к хард. В теории взаимозаменяемы, но жизнь вносит свои коррективы, и кто-то оказывается более сильным в ETL, а кому-то интереснее визуализация в Tableau пожелания в развитии каждого учитываются и мной, и тимлидом.

Работа по заявке идет с упором на 2 показателя: time-to-market (TTM) и customer satisfaction index (CSI). Причем сразу на проде, если речь об ETL-задачах в DWH. Тестовая зона, конечно же, есть, но подготовка данных на наших объемах занимает сильно больше времени, чем сама разработка. Важный момент: сообщения в чате наподобие ой, я оттранкейтил справочник... встречаются не чаще 1-2 раз в год и исправляются за 5-10 минут. Потерь невосстановимых, критичных для компании данных я не помню. В этом плане интереснее обращения от коллег из систем-источников на 100% реплицируемых в DWH с просьбой выслать из нашего бэкапа какую-нибудь таблицу фактов, которую массово проапдейтили, но что-то пошло не так За последний год такое было 2 раза.

Вы спросите, почему все так необычно устроено?

Кроме самого исчерпывающего объяснения

Так повелось в этом нашем лесу (с)

Есть очевидные минусы, с которыми мы умеем жить:

  • Быстроменяющаяся архитектура BI-решения является черным ящиком для всех смежных команд. Эта статья первый из шагов по расшариванию знаний о нашем решении.
  • Отток в команде потенциально критичен. Однако стабильность команды и преемственность нивелируют этот минус.

и плюсы:

  • Высокий TTM и высокая пропускная способность команды в целом. Весь проектный портфель компании (почти во всех проектах есть фичи на отдел) составляет 15-20% общего объема разработки отдела. Остальное прямые пожелания конечных бизнес-заказчиков, реализуемые с минимумом бюрократии.
  • Стабильно высокий CSI, демонстрирующий правильность выбранного подхода в организации разработки. Один раз в квартал мы проводим опрос среди бизнес-заказчиков. В 4Q20 из 43 респондентов ответили 21. По итогу получили 4,89 из 5. Это упавший CSI, хотя я предполагал падение до 4,5. Стандартно у нас ближе к 5. Объясняется это гибкостью в подходе к реализациям задумок бизнес-заказчиков и скоростью появления конечного результата с максимально эффективным использованием имеющихся инструментов/технологий.

В опросе CSI также можно оставить комментарий, например такой


Понимая все особенности подхода в BI-разработке, мы не планируем что-либо менять в обозримой перспективе.

Пользуясь случаем хочу поблагодарить #BI_TEAM за стабильно высокие результаты: ребята вы крутые, мне повезло работать со всеми вами! Спасибо.

6. Заключение


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

По идее здесь должны быть вакансии отдела, но извините full house) И даже есть небольшой лист ожидания Однако в соседних, не менее интересных, командах еще требуются люди. Буду рад комментариям нам есть куда расти.
Подробнее..

Как Microsoft Analysis Services финансовым аналитикам жизнь упростил

07.04.2021 16:22:59 | Автор: admin
Как мало пройдено дорог как много сделано отчетов

Введение


Василий, мы установили новый BI продукт, наш САМЙ ГЛАВНЙ от него просто в восторге!
Да, но я не знаю, как выгрузить данные для анализа из этой системы?! Он, похоже, только в html может что-то показывать.
Ничего, я думаю ты справишься, сам понимаешь, чем шире улыбка шефа, тем выше премия.
Но, Иван Васильевич, этот продукт в качестве источника данных использует только PDF файлы.
Зато он показывает шикарные разноцветные графики, у него анимация как в Звездных войнах, а руководство просто в восторге от его интерактивных возможностей. Там ещё и пасхалочка есть. Если три раза кликнуть в правом нижнем углу, появится Дарт Вейдер и споёт Марсельезу. Да и в целом, Вася, будь оптимистом! Хочешь анекдот в тему?

Что у вас запланировано на 1 января?
Катание на санках
А если снег не выпадет?
Это нас огорчит, но не остановит.

Не грусти Вася, принимайся за работу, а мне пора спешить утренняя планерка, эээ Daily Standup Meeting точнее, всё никак не могу запомнить.

Вася садится за свой рабочий стол и с грустью смотрит в монитор. Да уж, красивые графики, только толку от них? В Excel не выгрузить, с формулами не сверить, хоть бери тетрадку с ручкой и делай всё на бумаге. Плюс ещё как-то KPI на основе этого надо посчитать. Зато в ИТ отдел, говорят, художника взяли, чтобы он красивые отчеты для руководства оформлял. Глядя на новый продукт, Вася загрустил. В голове у него крутились пару строк из стихотворения C.А. Есенина Мне грустно на тебя смотреть:
Так мало пройдено дорог,
Так много сделано ошибок.

Ну что ж, оставим Васю на едине со своей болью и посмотрим на проблему шире. Видя переделку строк C.А. Есенина, которая вынесена в цитату к этой статье, мне кажется, что он не одинок в своих мыслях. Сложно понять, как работают современные BI системы и для кого их пишут то ли для аналитиков, то ли для руководителей. Очень много теории и информации, причём, в зависимости от источника, эта информация может противоречить самой себе. К этому стоит добавить обилие научных терминов и трудный для понимания язык описания. Сложно угадать с выбором, а цена ошибки велика, так как системы дорогие и работа с ними часто требует определенной квалификации. Понимая всё это, я решил поделиться своим опытом в BI сфере. Попытаюсь написать об этом простым языком и не вдаваться глубоко в теорию. Речь пойдет о Microsoft Analysis Services и о том, как он может решить часть проблем связанных с аналитической отчетностью. Другую часть этих проблем, я решил, написав специальную программу, которая позволяла формировать отчеты непосредственно в Excel, минуя HTML формы и минимизируя нагрузку на Web сервер, но о ней я уже писал тут http://personeltest.ru/aways/habr.com/ru/post/281703/, а тут даже видео снял: https://youtu.be/_csGSw-xyzQ. Приятного вам чтения.
Если лень читать, то есть кортокое видео (11 минут)
Создание OLAP-куба в Microsoft Analysis Services: https://youtu.be/f5DgG51KMf8
Но в этом видео далеко не всё то, о чём пойдёт речь далее!!!


Отчетность и её проблемы


Все началось с задачи, поставленной финансовым отделом крупного банка. Надо было создать систему отчетности, которая бы позволяла быстро и оперативно оценивать текущую ситуацию в организации. Для решения этой задачи мы взяли базу данных. Организовали в ней Хранилище (Data Warehouse), настроили процессы загрузки данных и установили систему отчетности. В качестве которой мы взяли SQL Server Reporting Services, так как этот продукт входил в MS Sharepoint, использовавшийся в тот момент в банке. В принципе всё работало, но у заказчика были претензии:

  • Претензия 1. HTML -> MS Excel: отчеты изначально формируются в HTML, а аналитики работают с MS Excel. Надо постоянно делать экспорт из одного формата в другой. При этом часто сбивается разметка и в Excel часто подгружается множество дополнительной информации, большой объём которой, в некоторых случаях, существенно влияет на производительность.
  • Претензия 2. Параметры для отчета: данные в отчетах зависят от параметров, причём при их изменении формируется новый отчет, который надо опять выгружать в Excel, что не всегда удобно.
  • Претензия 3. Добавление изменений в отчет: для того, чтобы что-то изменить в отчете, добавить новую колонку или создать группировку, надо обращаться к специалисту, который может поправить этот отчет на сервере.
  • Претензия 4. Анализ данных: отчеты получаются статическими и когда нужно посмотреть различные разрезы, поменять строки с колонками, отфильтровать или добавить, либо удалить какие-то значения, надо делать все эти манипуляции в Excel, что не всегда удобно, а порой и сложно, из-за проблем с производительностью компьютеров, на которых работают аналитики.

Стоит отметить, что сотрудники банка не рассматривали для себя никакого другого инструмента в качестве замены MS Excel. И на то были веские основания. Весь его функционал сложно чем-то заменить. К примеру, аналитики очень часто:

  • сверяли данные из базы по формулам, которые можно добавить в Excel,
  • хранили одни и те же срезы данных, сделанные в разные дни, с целью обнаружить ошибки,
  • добавляли туда свои данные,
  • писали различные скрипты

В общем использовали его на все 100%. Хотя были те, кто предлагал им что-то другое, точнее не столько предлагал, сколько заставлял. Как итог таких предложений, у нас в системе появились SAP BO, Oracle Reports Services и ряд других BI инструментов. Возможно, они в чем-то превосходили SQL Server Reporting Services, но суть работы с ними кардинально не изменилась:

  1. формируем отчет в HTML,
  2. экспортируем его в Excel,
  3. начинаем заниматься бесконечными танцами вокруг данных.

Требовалось что-то более кардинальное, не просто отчет, а некий набор данных, с которым удобно было бы работать.

Выход из ситуации


К найденному решению подтолкнули PivotTable в Excel



и PivotGrid от фирмы DevExpress ( https://demos.devexpress.com/blazor/PivotGrid).

Детально изучив эти решения вышли на MS Analysis Services и решили попробовать. Его можно использовать в Excel, и он может работать с Oracle, как с источником данных, что нас на тот момент устраивало. С точки зрения архитектуры, источником данных для него может служить что угодно, был бы нужный провайдер. Суть его в том, что он способен хранить в себе большие объемы данных со всеми их агрегациями и выдавать их клиенту максимально быстро. К Excel его можно легко подключить и манипулировать данными в Pivot Table.



В MS Analysis Services есть возможность партиционирования данных (хранение их в виде множества отдельных частей) и так же инкрементальное обновление данных. Это даёт ему возможность загружать данные из внешних систем небольшими кусочками и хранить их во множестве партиций. С точки зрения максимальных объемов, у него есть ограничения, но они довольно большие https://docs.microsoft.com/en-us/analysis-services/multidimensional-models/olap-physical/maximum-capacity-specifications-analysis-services?view=asallproducts-allversions.

MS Analysis Services является OLAP системой, которая использует отдельный сервер для хранения данных, либо части данных. Его плюсом является то, что он способен довольно быстро работать с миллионами записей, будучи установленным на обычный, современный компьютер. Так же он позволяет анализировать данные непосредственно в Excel и может заменить собой десятки отчетов на MS Reporting Services или ему подобных. Причем при работе с ним не надо писать и править различные запросы типа SQL, хотя при желании можно, только вместо SQL он использует MDX.

Правда есть тут и ложка дегтя. В Excel можно запросить разом очень большой объём данных и OLAP их вернет, но отобразить такой объем Excel не сможет, либо сможет, но работать при этом будет очень медленно. На первых порах это раздражало аналитиков, но поняв причину и настроив фильтры в Pivot Table эту проблему решили.

Секрет быстродействия MS Analysis Services, как и любой другой OLAP системы, кроется в архитектуре хранения данных. В нем все храниться в максимально подготовленном и оптимизированном для запросов виде. Такая подготовка требует времени и запись вновь пришедших данных в OLAP происходит не быстро, но, с другой стороны, чтение данных получается очень быстрым. Выходит долго пишем быстро читаем.

Немного теории


Чаще всего, когда анализируют данные их объединяют в группы, а сами группы так же объединяют в иерархии. Для примера возьмём торговую точку. С точки зрения бизнеса, интерес представляют продажи. То есть сколько товара было продано за день (1-группа), за месяц (2-ая) и за год (3-я). Где день месяц и год это разные уровни одной иерархии. Получается, что продажи за месяц это сумма всех продаж за все дни в месяце, а продажи за год это сумма продаж за все месяцы в этом году. Отсюда получается, что для получения максимального быстродействия, можно заранее собрать данные в группы и рассчитать агрегаты (в нашем примере суммы продаж) для каждого уровня иерархи. Вот на этом принципе и работают MS Analysis Services. Им достаточно сказать что надо считать, по какой формуле и на какие группы это можно разбить. Остальную работу они сделают сами. Тут немного о том как они это делают: http://citforum.ru/consulting/BI/molap_overview/node7.shtml. Стоит отметить, что в современных OLAP системах все агрегаты, чаще всего, не рассчитываются заранее. Это всё делается на лету, в момент запроса.

Теперь о терминах:


MS Analysis Services это одна из OLAP систем, где OLAP это аббревиатура online analytical processing. Дословно это означает интерактивная (online) аналитическая обработка данных. Со временем данная формулировка утратила свой первоначальный смысл, так как появились системы, способные обрабатывать данные с большой скоростью и передавать их пользователю без использования подходов, декларируемых в OLAP. Поэтому, сейчас есть более полное описание требований к системам, которые могут называться OLAP, это:


По своему опыту, могу сказать, что чем больше ваш OLAP куб удовлетворяет описанию Е.Ф. Кодда, тем лучше, как с точки зрения работы с ним, так и с точки зрения его создания.

Вкратце, OLAP это система хранения, организованная таким образом, чтобы данные в ней:

  • были взаимосвязаны,
  • собирались в иерархии,
  • имели функции агрегации по всему множеству иерархий

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

При построении OLAP выделяют Факты и Измерения. Факты это цифровые значения измеряемых величин. Измерения это сами измеряемые величины. Совокупность всех связанных между собой измерений, фактов и функций для их агрегации называют OLAP-кубом. Факты и Измерения связанны между собой. По типу связи выделяют 2 схемы организации хранения данных Звезда и Снежинка. Звезда это когда все измерения напрямую связаны с фактом, снежинка это когда есть измерения, которые связанны с фактом через другие измерения. Эти схемы можно создавать и просматривать в разделе Data Source Views в SSAS.







Создание OLAP-куба в Microsoft Analysis Services


Построение OLAP кубов делается через проект в Visual Studio. По большей части там реализована технология визуального программирования перетащить, кликнуть мышкой, настроить. Отсюда это проще показать, чем описать. Что я и сделал в моем видео: https://youtu.be/f5DgG51KMf8. Так же стоит отметить то, что Microsoft, в ознакомительных целях, предоставляет свои продукты бесплатно. Отсюда, посмотреть, как он работает можно на любом компьютере с ОС Windows 10, удовлетворяющем следующим требованиям: https://docs.microsoft.com/en-us/sql/sql-server/install/hardware-and-software-requirements-for-installing-sql-server-ver15?view=sql-server-ver15. Требования по ссылке к MS SQL Server, так как MS Analysis Services являются его частью.

Заключение


OLAP это относительно простой способ повысить скорость и удобство работы с данными. В данный момент существует множество решений, основанных на этой технологии. Я работал с MS Analysis Services (SSAS) и вот что мне в нём понравилось:

  • теоретически он позволяет работать с любым источником данных, не требуя при этом существенных затрат с точки зрения оборудования.
  • хранит данные на выделенном сервере, что снижает вероятность их потри, в отличие от систем, хранящих такие данные в памяти.
  • интегрирован с MS Excel, что облегчает работу с ним для тех, кто привык работать с данным продуктом.

В заключение, хочу сказать, что мне MS Analysis Services, в свое время, очень сильно помог и я надеюсь, что знания, которыми я поделился в этой статье, так же кому-то помогут.
Подробнее..

DB amp DWH Online Meetup 1509

07.09.2020 12:20:38 | Автор: admin
Первая онлайн-встреча сообщества DB & DWH Райффайзенбанка пройдет 15 сентября. Присоединяйтесь к нам, чтобы узнать про автоматизированное тестирование методом черного ящика и про переход на ETL-as-Service при помощи Informatica Power Center.



О чем будем говорить


Автоматизированное тестирование методом черного ящика в хранилище данных

Панюшкина Юлия и Колесников Дмитрий, Райффайзенбанк

Поделимся опытом, почему решили использовать тестирование методом черного ящика, как подготавливаем тестовые данные и как удалось автоматизировать наш подход к тестированию.

Перевод банковских процессов на ETL-as-Service при помощи Informatica Power Center

Александр Попов, ОТП-банк

Спикер расскажет о том, как планируется из монолитной платформы Informatica PowerCenter сделать полноценное общебанковское средство разработки ETL.

>>> Начнем митап в 16:00 (МСК).

Регистрируйтесь, чтобы получить ссылку на трансляцию: письмо со ссылкой придет вам на почту. Мы вас ждем, до встречи online!
Подробнее..

Apache Airflow делаем ETL проще

27.07.2020 12:16:39 | Автор: admin

Привет, я Дмитрий Логвиненко Data Engineer отдела аналитики группы компаний Везёт.


Я расскажу вам о замечательном инструменте для разработки ETL-процессов Apache Airflow. Но Airflow настолько универсален и многогранен, что вам стоит присмотреться к нему даже если вы не занимаетесь потоками данных, а имеете потребность периодически запускать какие-либо процессы и следить за их выполнением.


И да, я буду не только рассказывать, но и показывать: в программе много кода, скриншотов и рекомендаций.



Что обычно видишь, когда гуглишь слово Airflow / Wikimedia Commons


Введение


Apache Airflow он прямо как Django:


  • написан на Python,
  • есть отличная админка,
  • неограниченно расширяем,

только лучше, да и сделан совсем для других целей, а именно (как написано до ката):


  • запуск и мониторинг задач на неограниченном количестве машин (сколько вам позволит Celery/Kubernetes и ваша совесть)
  • с динамической генерацией workflow из очень легкого для написания и восприятия Python-кода
  • и возможностью связывать друг с друг любые базы данных и API с помощью как готовых компонентов, так и самодельных плагинов (что делается чрезвычайно просто).

Мы используем Apache Airflow так:


  • собираем данные из различных источников (множество инстансов SQL Server и PostgreSQL, различные API с метриками приложений, даже 1С) в DWH и ODS (у нас это Vertica и Clickhouse).
  • как продвинутый cron, который запускает процессы консолидации данных на ODS, а также следит за их обслуживанием.

До недавнего времени наши потребности покрывал один небольшой сервер на 32 ядрах и 50 GB оперативки. В Airflow при этом работает:


  • более 200 дагов (собственно workflows, в которые мы набили задачки),
  • в каждом в среднем по 70 тасков,
  • запускается это добро (тоже в среднем) раз в час.

А о том, как мы расширялись, я напишу ниже, а сейчас давайте определим ber-задачу, которую мы будем решать:


Есть три исходных SQL Serverа, на каждом по 50 баз данных инстансов одного проекта, соответственно, структура у них одинаковая (почти везде, муа-ха-ха), а значит в каждой есть таблица Orders (благо таблицу с таким названием можно затолкать в любой бизнес). Мы забираем данные, добавляя служебные поля (сервер-источник, база-источник, идентификатор ETL-задачи) и наивным образом бросим их в, скажем, Vertica.

Поехали!


Часть основная, практическая (и немного теоретическая)


Зачем оно нам (и вам)


Когда деревья были большими, а я был простым SQL-щиком в одном российском ритейле, мы шпарили ETL-процессы aka потоки данных с помощью двух доступных нам средств:


  • Informatica Power Center крайне развесистая система, чрезвычайно производительная, со своими железками, собственным версионированием. Использовал я дай бог 1% её возможностей. Почему? Ну, во-первых, этот интерфейс где-то из нулевых психически давил на нас. Во-вторых, эта штуковина заточена под чрезвычайно навороченные процессы, яростное переиспользование компонентов и другие очень-важные-энтерпрайз-фишечки. Про то что стоит она, как крыло Airbus A380/год, мы промолчим.


    Осторожно, скриншот может сделать людям младше 30 немного больно




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




Мы безусловно искали выходы. Дело даже почти дошло до самописного генератора SSIS-пакетов...


а потом меня нашла новая работа. А на ней меня настиг Apache Airflow.


Когда я узнал, что описания ETL-процессов это простой Python-код, я только что не плясал от радости. Вот так потоки данных подверглись версионированию и диффу, а ссыпать таблицы с единой структурой из сотни баз данных в один таргет стало делом Python-кода в полтора-два 13 экрана.


Собираем кластер


Давайте не устраивать совсем уж детский сад, и не говорить тут о совершенно очевидных вещах, вроде установки Airflow, выбранной вами БД, Celery и других дел, описанных в доках.


Чтобы мы могли сразу приступить к экспериментам, я набросал docker-compose.yml в котором:


  • Поднимем собственно Airflow: Scheduler, Webserver. Там же будет крутится Flower для мониторинга Celery-задач (потому что его уже затолкали в apache/airflow:1.10.10-python3.7, а мы и не против);
  • PostgreSQL, в который Airflow будет писать свою служебную информацию (данные планировщика, статистика выполнения и т. д.), а Celery отмечать завершенные таски;
  • Redis, который будет выступать брокером задач для Celery;
  • Celery worker, который и займется непосредственным выполнением задачек.
  • В папку ./dags мы будет складывать наши файлы с описанием дагов. Они будут подхватываться на лету, поэтому передёргивать весь стек после каждого чиха не нужно.

Кое-где код в примерах приведен не полностью (чтобы не загромождать текст), а где-то он модифицируется в процессе. Цельные работающие примеры кода можно посмотреть в репозитории https://github.com/dm-logv/airflow-tutorial.

docker-compose.yml
version: '3.4'x-airflow-config: &airflow-config  AIRFLOW__CORE__DAGS_FOLDER: /dags  AIRFLOW__CORE__EXECUTOR: CeleryExecutor  AIRFLOW__CORE__FERNET_KEY: MJNz36Q8222VOQhBOmBROFrmeSxNOgTCMaVp2_HOtE0=  AIRFLOW__CORE__HOSTNAME_CALLABLE: airflow.utils.net:get_host_ip_address  AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgres+psycopg2://airflow:airflow@airflow-db:5432/airflow  AIRFLOW__CORE__PARALLELISM: 128  AIRFLOW__CORE__DAG_CONCURRENCY: 16  AIRFLOW__CORE__MAX_ACTIVE_RUNS_PER_DAG: 4  AIRFLOW__CORE__LOAD_EXAMPLES: 'False'  AIRFLOW__CORE__LOAD_DEFAULT_CONNECTIONS: 'False'  AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_RETRY: 'False'  AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_FAILURE: 'False'  AIRFLOW__CELERY__BROKER_URL: redis://broker:6379/0  AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@airflow-db/airflowx-airflow-base: &airflow-base  image: apache/airflow:1.10.10-python3.7  entrypoint: /bin/bash  restart: always  volumes:    - ./dags:/dags    - ./requirements.txt:/requirements.txtservices:  # Redis as a Celery broker  broker:    image: redis:6.0.5-alpine  # DB for the Airflow metadata  airflow-db:    image: postgres:10.13-alpine    environment:      - POSTGRES_USER=airflow      - POSTGRES_PASSWORD=airflow      - POSTGRES_DB=airflow    volumes:      - ./db:/var/lib/postgresql/data  # Main container with Airflow Webserver, Scheduler, Celery Flower  airflow:    <<: *airflow-base    environment:      <<: *airflow-config      AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: 30      AIRFLOW__SCHEDULER__CATCHUP_BY_DEFAULT: 'False'      AIRFLOW__SCHEDULER__MAX_THREADS: 8      AIRFLOW__WEBSERVER__LOG_FETCH_TIMEOUT_SEC: 10    depends_on:      - airflow-db      - broker    command: >      -c " sleep 10 &&           pip install --user -r /requirements.txt &&           /entrypoint initdb &&          (/entrypoint webserver &) &&          (/entrypoint flower &) &&           /entrypoint scheduler"    ports:      # Celery Flower      - 5555:5555      # Airflow Webserver      - 8080:8080  # Celery worker, will be scaled using `--scale=n`  worker:    <<: *airflow-base    environment:      <<: *airflow-config    command: >      -c " sleep 10 &&           pip install --user -r /requirements.txt &&           /entrypoint worker"    depends_on:      - airflow      - airflow-db      - broker

Примечания:


  • В сборке композа я во многом опирался на известный образ puckel/docker-airflow обязательно посмотрите. Может, вам в жизни больше ничего и не понадобится.
  • Все настройки Airflow доступны не только через airflow.cfg, но и через переменные среды (слава разработчикам), чем я злостно воспользовался.
  • Естественно, он не production-ready: я намеренно не ставил heartbeats на контейнеры, не заморачивался с безопасностью. Но минимум, подходящий для наших экспериментиков я сделал.
  • Обратите внимание, что:
    • Папка с дагами должна быть доступна как планировщику, так и воркерам.
    • То же самое касается и всех сторонних библиотек они все должны быть установлены на машины с шедулером и воркерами.

Ну а теперь просто:


$ docker-compose up --scale worker=3

После того, как всё поднимется, можно смотреть на веб-интерфейсы:



Основные понятия


Если вы ничего не поняли во всех этих дагах, то вот краткий словарик:


  • Scheduler самый главный дядька в Airflow, контролирующий, чтобы вкалывали роботы, а не человек: следит за расписанием, обновляет даги, запускает таски.


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


  • DAG (он же даг) направленный ацикличный граф, но такое определение мало кому что скажет, а по сути это контейнер для взаимодействующих друг с другом тасков (см. ниже) или аналог Package в SSIS и Workflow в Informatica.


    Помимо дагов еще могут быть сабдаги, но мы до них скорее всего не доберёмся.


  • DAG Run инициализированный даг, которому присвоен свой execution_date. Даграны одного дага могут вполне работать параллельно (если вы, конечно, сделали свои таски идемпотентными).


  • Operator это кусочки кода, ответственные за выполнение какого-либо конкретного действия. Есть три типа операторов:


    • action, как например наш любимый PythonOperator, который в силах выполнить любой (валидный) Python-код;
    • transfer, которые перевозят данные с места на место, скажем, MsSqlToHiveTransfer;
    • sensor же позволит реагировать или притормозить дальнейшее выполнение дага до наступления какого-либо события. HttpSensor может дергать указанный эндпойнт, и когда дождется нужный ответ, запустить трансфер GoogleCloudStorageToS3Operator. Пытливый ум спросит: зачем? Ведь можно делать повторы прямо в операторе! А затем, чтобы не забивать пул тасков подвисшими операторами. Сенсор запускается, проверяет и умирает до следующей попытки.

  • Task объявленные операторы вне зависимости от типа и прикрепленные к дагу повышаются до чина таска.


  • Task instance когда генерал-планировщик решил, что таски пора отправлять в бой на исполнители-воркеры (прямо на месте, если мы используем LocalExecutor или на удалённую ноду в случае с CeleryExecutor), он назначает им контекст (т. е. комплект переменных параметров выполнения), разворачивает шаблоны команд или запросов и складывает их в пул.



Генерируем таски


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


Итак, в простейшем виде подобный даг будет выглядеть так:


from datetime import timedelta, datetimefrom airflow import DAGfrom airflow.operators.python_operator import PythonOperatorfrom commons.datasources import sql_server_dsdag = DAG('orders',          schedule_interval=timedelta(hours=6),          start_date=datetime(2020, 7, 8, 0))def workflow(**context):    print(context)for conn_id, schema in sql_server_ds:    PythonOperator(        task_id=schema,        python_callable=workflow,        provide_context=True,        dag=dag)

Давайте разбираться:


  • Сперва импортируем нужные либы и кое что ещё;
  • sql_server_ds это List[namedtuple[str, str]] с именами коннектов из Airflow Connections и базами данных из которых мы будем забирать нашу табличку;
  • dag объявление нашего дага, которое обязательно должно лежать в globals(), иначе Airflow его не найдет. Дагу также нужно сказать:
    • что его зовут orders это имя потом будет маячить в веб-интерфейсе,
    • что работать он будет, начиная с полуночи восьмого июля,
    • а запускать он должен, примерно каждые 6 часов (для крутых парней здесь вместо timedelta() допустима cron-строка 0 0 0/6 ? * * *, для менее крутых выражение вроде @daily);
  • workflow() будет делать основную работу, но не сейчас. Сейчас мы просто высыпем наш контекст в лог.
  • А теперь простая магия создания тасков:
    • пробегаем по нашим источникам;
    • инициализируем PythonOperator, который будет выполнять нашу пустышку workflow(). Не забывайте указывать уникальное (в рамках дага) имя таска и подвязывать сам даг. Флаг provide_context в свою очередь насыпет в функцию дополнительных аргументов, которые мы бережно соберём с помощью **context.

Пока на этом всё. Что мы получили:


  • новый даг в веб-интерфейсе,
  • полторы сотни тасков, которые будут выполняться параллельно (если то позволят настройки Airflow, Celery и мощности серверов).

Ну, почти получили.



Зависимости кто будет ставить?


Чтобы всё это дело упростить я вкорячил в docker-compose.yml обработку requirements.txt на всех нодах.


Вот теперь понеслась:



Серые квадратики task instances, обработанные планировщиком.


Немного ждем, задачи расхватывают воркеры:



Зеленые, понятное дело, успешно отработавшие. Красные не очень успешно.


Кстати, на нашем проде никакой папки ./dags, синхронизирующейся между машинами нет всё даги лежат в git на нашем Gitlab, а Gitlab CI раскладывает обновления на машины при мёрдже в master.

Немного о Flower


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


Самая первая страничка с суммарной информацией по нодам-воркерам:



Самая насыщенная страничка с задачами, отправившимися в работу:



Самая скучная страничка с состоянием нашего брокера:



Самая яркая страничка с графиками состояния тасков и их временем выполнения:



Догружаем недогруженное


Итак, все таски отработали, можно уносить раненых.



А раненых оказалось немало по тем или иным причинами. В случае правильного использования Airflow вот эти самые квадраты говорят о том, что данные определенно не доехали.


Нужно смотреть лог и перезапускать упавшие task instances.


Жмякнув на любой квадрат, увидим доступные нам действия:



Можно взять, и сделать Clear упавшему. То есть, мы забываем о том, что там что-то завалилось, и тот же самый инстанс таска уйдет планировщику.



Понятно, что делать так мышкой со всеми красными квадратами не очень гуманно не этого мы ждем от Airflow. Естественно, у нас есть оружие массового поражения: Browse/Task Instances



Выберем всё разом и обнулим нажмем правильный пункт:



После очистки наши такси выглядят так (они уже ждут не дождутся, когда шедулер их запланирует):



Соединения, хуки и прочие переменные


Самое время посмотреть на следующий DAG, update_reports.py:


from collections import namedtuplefrom datetime import datetime, timedeltafrom textwrap import dedentfrom airflow import DAGfrom airflow.contrib.operators.vertica_operator import VerticaOperatorfrom airflow.operators.email_operator import EmailOperatorfrom airflow.utils.trigger_rule import TriggerRulefrom commons.operators import TelegramBotSendMessagedag = DAG('update_reports',          start_date=datetime(2020, 6, 7, 6),          schedule_interval=timedelta(days=1),          default_args={'retries': 3, 'retry_delay': timedelta(seconds=10)})Report = namedtuple('Report', 'source target')reports = [Report(f'{table}_view', table) for table in [    'reports.city_orders',    'reports.client_calls',    'reports.client_rates',    'reports.daily_orders',    'reports.order_duration']]email = EmailOperator(    task_id='email_success', dag=dag,    to='{{ var.value.all_the_kings_men }}',    subject='DWH Reports updated',    html_content=dedent("""Господа хорошие, отчеты обновлены"""),    trigger_rule=TriggerRule.ALL_SUCCESS)tg = TelegramBotSendMessage(    task_id='telegram_fail', dag=dag,    tg_bot_conn_id='tg_main',    chat_id='{{ var.value.failures_chat }}',    message=dedent("""\         Наташ, просыпайся, мы {{ dag.dag_id }} уронили        """),    trigger_rule=TriggerRule.ONE_FAILED)for source, target in reports:    queries = [f"TRUNCATE TABLE {target}",               f"INSERT INTO {target} SELECT * FROM {source}"]    report_update = VerticaOperator(        task_id=target.replace('reports.', ''),        sql=queries, vertica_conn_id='dwh',        task_concurrency=1, dag=dag)    report_update >> [email, tg]

Все ведь когда-нибудь делали обновлялку отчетов? Это снова она: есть список источников, откуда забрать данные; есть список, куда положить; не забываем посигналить, когда всё случилось или сломалось (ну это не про нас, нет).


Давайте снова пройдемся по файлу и посмотрим на новые непонятные штуки:


  • from commons.operators import TelegramBotSendMessage нам ничто не мешает делать свои операторы, чем мы и воспользовались, сделав небольшую обёрточку для отправки сообщений в Разблокированный. (Об этом операторе мы еще поговорим ниже);
  • default_args={} даг может раздавать одни и те же аргументы всем своим операторам;
  • to='{{ var.value.all_the_kings_men }}' поле to у нас будет не захардкоженным, а формируемым динамически с помощью Jinja и переменной со списком email-ов, которую я заботливо положил в Admin/Variables;
  • trigger_rule=TriggerRule.ALL_SUCCESS условие запуска оператора. В нашем случае, письмо полетит боссам только если все зависимости отработали успешно;
  • tg_bot_conn_id='tg_main' аргументы conn_id принимают в себя идентификаторы соединений, которые мы создаем в Admin/Connections;
  • trigger_rule=TriggerRule.ONE_FAILED сообщения в Telegram улетят только при наличии упавших тасков;
  • task_concurrency=1 запрещаем одновременный запуск нескольких task instances одного таска. В противном случае, мы получим одновременный запуск нескольких VerticaOperator (смотрящих на одну таблицу);
  • report_update >> [email, tg] все VerticaOperator сойдутся в отправке письма и сообщения, вот так:


    Но так как у операторов-нотификаторов стоят разные условия запуска, работать будет только один. В Tree View всё выглядит несколько менее наглядно:



Скажу пару слов о макросах и их друзьях переменных.


Макросы это Jinja-плейсхолдеры, которые могут подставлять разную полезную информацию в аргументы операторов. Например, так:


SELECT    id,    payment_dtm,    payment_type,    client_idFROM orders.paymentsWHERE    payment_dtm::DATE = '{{ ds }}'::DATE

{{ ds }} развернется в содержимое переменной контекста execution_date в формате YYYY-MM-DD: 2020-07-14. Самое приятное, что переменные контекста прибиваются гвоздями к определенному инстансу таска (квадратику в Tree View), и при перезапуске плейсхолдеры раскроются в те же самые значения.


Присвоенные значения можно смотреть с помощью кнопки Rendered на каждом таск-инстансе. Вот так у таска с отправкой письма:



А так у таски с отправкой сообщения:



Полный список встроенных макросов для последней доступной версии доступен здесь: Macros Reference


Более того, с помощью плагинов, мы можем объявлять собственные макросы, но это уже совсем другая история.


Помимо предопределенных штук, мы можем подставлять значения своих переменных (выше в коде я уже этим воспользовался). Создадим в Admin/Variables пару штук:



Всё, можно пользоваться:


TelegramBotSendMessage(chat_id='{{ var.value.failures_chat }}')

В значении может быть скаляр, а может лежать и JSON. В случае JSON-а:


bot_config{    "bot": {        "token": 881hskdfASDA16641,        "name": "Verter"    },    "service": "TG"}

просто используем путь к нужному ключу: {{ var.json.bot_config.bot.token }}.


Скажу буквально одно слово и покажу один скриншот про соединения. Тут всё элементарно: на странице Admin/Connections создаем соединение, складываем туда наши логины/пароли и более специфичные параметры. Вот так:



Пароли можно шифровать (более тщательно, чем в варианте по умолчанию), а можно не указывать тип соединения (как я сделал для tg_main) дело в том, что список типов зашит в моделях Airflow и расширению без влезания в исходники не поддается (если вдруг я чего-то не догуглил прошу меня поправить), но получить креды просто по имени нам ничто не помешает.


А еще можно сделать несколько соединений с одним именем: в таком случае метод BaseHook.get_connection(), который достает нам соединения по имени, будет отдавать случайного из нескольких тёзок (было бы логичнее сделать Round Robin, но оставим это на совести разработчиков Airflow).


Variables и Connections, безусловно, классные средства, но важно не потерять баланс: какие части ваших потоков вы храните собственно в коде, а какие отдаете на хранение Airflow. C одной стороны быстро поменять значение, например, ящик рассылки, может быть удобно через UI. А с другой это всё-таки возврат к мышеклику, от которого мы (я) хотели избавиться.

Работа с соединениями это одна из задач хуков. Вообще хуки Airflow это точки подключения его к сторонним сервисам и библиотекам. К примеру, JiraHook откроет для нас клиент для взаимодействия с Jira (можно задачки подвигать туда-сюда), а с помощью SambaHook можно запушить локальный файл на smb-точку.


Разбираем кастомный оператор


И мы вплотную подобрались к тому, чтобы посмотреть на то, как сделан TelegramBotSendMessage


Код commons/operators.py с собственно оператором:


from typing import Unionfrom airflow.operators import BaseOperatorfrom commons.hooks import TelegramBotHook, TelegramBotclass TelegramBotSendMessage(BaseOperator):    """Send message to chat_id using TelegramBotHook    Example:        >>> TelegramBotSendMessage(        ...     task_id='telegram_fail', dag=dag,        ...     tg_bot_conn_id='tg_bot_default',        ...     chat_id='{{ var.value.all_the_young_dudes_chat }}',        ...     message='{{ dag.dag_id }} failed :(',        ...     trigger_rule=TriggerRule.ONE_FAILED)    """    template_fields = ['chat_id', 'message']    def __init__(self,                 chat_id: Union[int, str],                 message: str,                 tg_bot_conn_id: str = 'tg_bot_default',                 *args, **kwargs):        super().__init__(*args, **kwargs)        self._hook = TelegramBotHook(tg_bot_conn_id)        self.client: TelegramBot = self._hook.client        self.chat_id = chat_id        self.message = message    def execute(self, context):        print(f'Send "{self.message}" to the chat {self.chat_id}')        self.client.send_message(chat_id=self.chat_id,                                 message=self.message)

Здесь, как и остальное в Airflow, всё очень просто:


  • Отнаследовались от BaseOperator, который реализует довольно много Airflow-специфичных штук (посмотрите на досуге)
  • Объявили поля template_fields, в которых Jinja будет искать макросы для обработки.
  • Организовали правильные аргументы для __init__(), расставили умолчания, где надо.
  • Об инициализации предка тоже не забыли.
  • Открыли соответствующий хук TelegramBotHook, получили от него объект-клиент.
  • Оверрайднули (переопределили) метод BaseOperator.execute(), который Airfow будет подергивать, когда наступит время запускать оператор в нем мы и реализуем основное действие, на забыв залогироваться. (Логируемся, кстати, прямо в stdout и stderr Airflow всё перехватит, красиво обернет, разложит, куда надо.)

Давайте смотреть, что у нас в commons/hooks.py. Первая часть файлика, с самим хуком:


from typing import Unionfrom airflow.hooks.base_hook import BaseHookfrom requests_toolbelt.sessions import BaseUrlSessionclass TelegramBotHook(BaseHook):    """Telegram Bot API hook    Note: add a connection with empty Conn Type and don't forget    to fill Extra:        {"bot_token": "YOuRAwEsomeBOtToKen"}    """    def __init__(self,                 tg_bot_conn_id='tg_bot_default'):        super().__init__(tg_bot_conn_id)        self.tg_bot_conn_id = tg_bot_conn_id        self.tg_bot_token = None        self.client = None        self.get_conn()    def get_conn(self):        extra = self.get_connection(self.tg_bot_conn_id).extra_dejson        self.tg_bot_token = extra['bot_token']        self.client = TelegramBot(self.tg_bot_token)        return self.client

Я даже не знаю, что тут можно объяснять, просто отмечу важные моменты:


  • Наследуемся, думаем над аргументами в большинстве случаев он будет один: conn_id;
  • Переопределяем стандартные методы: я ограничился get_conn(), в котором я получаю параметры соединения по имени и всего-навсего достаю секцию extra (это поле для JSON), в которую я (по своей же инструкции!) положил токен Telegram-бота: {"bot_token": "YOuRAwEsomeBOtToKen"}.
  • Создаю экземпляр нашего TelegramBot, отдавая ему уже конкретный токен.

Вот и всё. Получить клиент из хука можно c помощью TelegramBotHook().clent или TelegramBotHook().get_conn().


И вторая часть файлика, в котором я сделать микрообёрточку для Telegram REST API, чтобы не тащить тот же python-telegram-bot ради одного метода sendMessage.


class TelegramBot:    """Telegram Bot API wrapper    Examples:        >>> TelegramBot('YOuRAwEsomeBOtToKen', '@myprettydebugchat').send_message('Hi, darling')        >>> TelegramBot('YOuRAwEsomeBOtToKen').send_message('Hi, darling', chat_id=-1762374628374)    """    API_ENDPOINT = 'https://api.telegram.org/bot{}/'    def __init__(self, tg_bot_token: str, chat_id: Union[int, str] = None):        self._base_url = TelegramBot.API_ENDPOINT.format(tg_bot_token)        self.session = BaseUrlSession(self._base_url)        self.chat_id = chat_id    def send_message(self, message: str, chat_id: Union[int, str] = None):        method = 'sendMessage'        payload = {'chat_id': chat_id or self.chat_id,                   'text': message,                   'parse_mode': 'MarkdownV2'}        response = self.session.post(method, data=payload).json()        if not response.get('ok'):            raise TelegramBotException(response)class TelegramBotException(Exception):    def __init__(self, *args, **kwargs):        super().__init__((args, kwargs))

Правильный путь сложить всё это: TelegramBotSendMessage, TelegramBotHook, TelegramBot в плагин, положить в общедоступный репозиторий, и отдать в Open Source.

Пока мы всё это изучали, наши обновления отчетов успели успешно завалиться и отправить мне в канал сообщение об ошибке. Пойду проверять, что опять не так...



В нашем даге что-то сломалось! А ни этого ли мы ждали? Именно!


Наливать-то будешь?


Чувствуете, что-то я пропустил? Вроде бы обещал данные из SQL Server в Vertica переливать, и тут взял и съехал с темы, негодяй!


Злодеяние это было намеренным, я просто обязан был расшифровать вам кое-какую терминологию. Теперь можно ехать дальше.


План у нас был такой:


  1. Сделать даг
  2. Нагенерить таски
  3. Посмотреть, как всё красиво
  4. Присваивать заливкам номера сессий
  5. Забрать данные из SQL Server
  6. Положить данные в Vertica
  7. Собрать статистику

Итак, чтобы всё это запустить, я сделал маленькое дополнение к нашему docker-compose.yml:


docker-compose.db.yml
version: '3.4'x-mssql-base: &mssql-base  image: mcr.microsoft.com/mssql/server:2017-CU21-ubuntu-16.04  restart: always  environment:    ACCEPT_EULA: Y    MSSQL_PID: Express    SA_PASSWORD: SayThanksToSatiaAt2020    MSSQL_MEMORY_LIMIT_MB: 1024services:  dwh:    image: jbfavre/vertica:9.2.0-7_ubuntu-16.04  mssql_0:    <<: *mssql-base  mssql_1:    <<: *mssql-base  mssql_2:    <<: *mssql-base  mssql_init:    image: mio101/py3-sql-db-client-base    command: python3 ./mssql_init.py    depends_on:      - mssql_0      - mssql_1      - mssql_2    environment:      SA_PASSWORD: SayThanksToSatiaAt2020    volumes:      - ./mssql_init.py:/mssql_init.py      - ./dags/commons/datasources.py:/commons/datasources.py

Там мы поднимаем:


  • Vertica как хост dwh с самыми дефолтными настройками,
  • три экземпляра SQL Server,
  • наполняем базы в последних кое-какими данными (ни в коем случае не заглядывайте в mssql_init.py!)

Запускаем всё добро с помощью чуть более сложной, чем в прошлый раз, команды:


$ docker-compose -f docker-compose.yml -f docker-compose.db.yml up --scale worker=3

Что нагенерировал наш чудорандомайзер, можно, воспользовавшись пунктом Data Profiling/Ad Hoc Query:



Главное, не показывать это аналитикам


Подробно останавливаться на ETL-сессиях я не буду, там всё тривиально: делаем базу, в ней табличку, оборачиваем всё менеджером контекста, и теперь делаем так:


with Session(task_name) as session:    print('Load', session.id, 'started')    # Load worflow    ...    session.successful = True    session.loaded_rows = 15

session.py
from sys import stderrclass Session:    """ETL workflow session    Example:        with Session(task_name) as session:            print(session.id)            session.successful = True            session.loaded_rows = 15            session.comment = 'Well done'    """    def __init__(self, connection, task_name):        self.connection = connection        self.connection.autocommit = True        self._task_name = task_name        self._id = None        self.loaded_rows = None        self.successful = None        self.comment = None    def __enter__(self):        return self.open()    def __exit__(self, exc_type, exc_val, exc_tb):        if any(exc_type, exc_val, exc_tb):            self.successful = False            self.comment = f'{exc_type}: {exc_val}\n{exc_tb}'            print(exc_type, exc_val, exc_tb, file=stderr)        self.close()    def __repr__(self):        return (f'<{self.__class__.__name__} '                 f'id={self.id} '                 f'task_name="{self.task_name}">')    @property    def task_name(self):        return self._task_name    @property    def id(self):        return self._id    def _execute(self, query, *args):        with self.connection.cursor() as cursor:            cursor.execute(query, args)            return cursor.fetchone()[0]    def _create(self):        query = """            CREATE TABLE IF NOT EXISTS sessions (                id          SERIAL       NOT NULL PRIMARY KEY,                task_name   VARCHAR(200) NOT NULL,                started     TIMESTAMPTZ  NOT NULL DEFAULT current_timestamp,                finished    TIMESTAMPTZ           DEFAULT current_timestamp,                successful  BOOL,                loaded_rows INT,                comment     VARCHAR(500)            );            """        self._execute(query)    def open(self):        query = """            INSERT INTO sessions (task_name, finished)            VALUES (%s, NULL)            RETURNING id;            """        self._id = self._execute(query, self.task_name)        print(self, 'opened')        return self    def close(self):        if not self._id:            raise SessionClosedError('Session is not open')        query = """            UPDATE sessions            SET                finished    = DEFAULT,                successful  = %s,                loaded_rows = %s,                comment     = %s            WHERE                id = %s            RETURNING id;            """        self._execute(query, self.successful, self.loaded_rows,                      self.comment, self.id)        print(self, 'closed',              ', successful: ', self.successful,              ', Loaded: ', self.loaded_rows,              ', comment:', self.comment)class SessionError(Exception):    passclass SessionClosedError(SessionError):    pass

Настала пора забрать наши данные из наших полутора сотен таблиц. Сделаем это с помощью очень незатейливых строчек:


source_conn = MsSqlHook(mssql_conn_id=src_conn_id, schema=src_schema).get_conn()query = f"""    SELECT         id, start_time, end_time, type, data    FROM dbo.Orders    WHERE        CONVERT(DATE, start_time) = '{dt}'    """df = pd.read_sql_query(query, source_conn)

  1. С помощью хука получим из Airflow pymssql-коннект
  2. В запрос подставим ограничение в виде даты в функцию её подбросит шаблонизатор.
  3. Скармливаем наш запрос pandas, который достанет для нас DataFrame он нам пригодится в дальнейшем.

Я использую подстановку {dt} вместо параметра запроса %s не потому, что я злобный Буратино, а потому что pandas не может совладать с pymssql и подсовывает последнему params: List, хотя тот очень хочет tuple.
Также обратите внимание, что разработчик pymssql решил больше его не поддерживать, и самое время съехать на pyodbc.

Посмотрим, чем Airflow нашпиговал аргументы наших функций:



Если данных не оказалось, то продолжать смысла нет. Но считать заливку успешной тоже странно. Но это и не ошибка. А-а-а, что делать?! А вот что:


if df.empty:    raise AirflowSkipException('No rows to load')

AirflowSkipException скажет Airflow, что ошибки, собственно нет, а таск мы пропускаем. В интерфейсе будет не зеленый и не красный квадратик, а цвета pink.


Подбросим нашим данным несколько колонок:


df['etl_source'] = src_schemadf['etl_id'] = session.iddf['hash_id'] = hash_pandas_object(df[['etl_source', 'id']])

А именно:


  • БД, из которой мы забрали заказы,
  • Идентификатор нашей заливающей сессии (она будет разной на каждый таск),
  • Хэш от источника и идентификатора заказа чтобы в конечной базе (где всё ссыпется в одну таблицу) у нас был уникальный идентификатор заказа.

Остался предпоследний шаг: залить всё в Vertica. А, как ни странно, один из самых эффектных эффективных способов сделать это через CSV!


# Export data to CSV bufferbuffer = StringIO()df.to_csv(buffer,          index=False, sep='|', na_rep='NUL', quoting=csv.QUOTE_MINIMAL,          header=False, float_format='%.8f', doublequote=False, escapechar='\\')buffer.seek(0)# Push CSVtarget_conn = VerticaHook(vertica_conn_id=target_conn_id).get_conn()copy_stmt = f"""    COPY {target_table}({df.columns.to_list()})     FROM STDIN     DELIMITER '|'     ENCLOSED '"'     ABORT ON ERROR     NULL 'NUL'    """cursor = target_conn.cursor()cursor.copy(copy_stmt, buffer)

  1. Мы делаем спецприёмник StringIO.
  2. pandas любезно сложит в него наш DataFrame в виде CSV-строк.
  3. Откроем соединение к нашей любимой Vertica хуком.
  4. А теперь с помощью copy() отправим наши данные прямо в Вертику!

Из драйвера заберем, сколько строчек засыпалось, и скажем менеджеру сессии, что всё ОК:


session.loaded_rows = cursor.rowcountsession.successful = True

Вот и всё.


На проде мы создаем целевую табличку вручную. Здесь же я позволил себе небольшой автомат:

create_schema_query = f'CREATE SCHEMA IF NOT EXISTS {target_schema};'create_table_query = f"""    CREATE TABLE IF NOT EXISTS {target_schema}.{target_table} (         id         INT,         start_time TIMESTAMP,         end_time   TIMESTAMP,         type       INT,         data       VARCHAR(32),         etl_source VARCHAR(200),         etl_id     INT,         hash_id    INT PRIMARY KEY     );"""create_table = VerticaOperator(    task_id='create_target',    sql=[create_schema_query,         create_table_query],    vertica_conn_id=target_conn_id,    task_concurrency=1,    dag=dag)

Я с помощью VerticaOperator() создаю схему БД и таблицу (если их еще нет, естественно). Главное, правильно расставить зависимости:

for conn_id, schema in sql_server_ds:    load = PythonOperator(        task_id=schema,        python_callable=workflow,        op_kwargs={            'src_conn_id': conn_id,            'src_schema': schema,            'dt': '{{ ds }}',            'target_conn_id': target_conn_id,            'target_table': f'{target_schema}.{target_table}'},        dag=dag)    create_table >> load

Подводим итоги


Ну вот, сказал мышонок, не правда ли, теперь
Ты убедился, что в лесу я самый страшный зверь?

Джулия Дональдсон, Груффало


Думаю, если бы мы с моими коллегами устроили соревнование: кто быстрее составит и запустит с нуля ETL-процесс: они со своими SSIS и мышкой и я с Airflow А потом бы мы еще сравнили удобство сопровождения Ух, думаю, вы согласитесь, что я обойду их по всем фронтам!


Если же чуть-чуть посерьезнее, то Apache Airflow за счет описания процессов в виде программного кода сделал мою работу гораздо удобнее и приятнее.


Его же неограниченная расширяемость: как в плане плагинов, так и предрасположенность к масштабируемости даёт вам возможность применять Airflow практически в любой области: хоть в полном цикле сбора, подготовки и обработки данных, хоть в запуске ракет (на Марс, конечно же).


Часть заключительная, справочно-информационная


Грабли, которые мы собрали за вас


  • start_date. Да, это уже локальный мемасик. Через главный аргумент дага start_date проходят все. Кратко, если указать в start_date текущую дату, а в schedule_interval один день, то DAG запустится завтра не раньше.


    start_date = datetime(2020, 7, 7, 0, 1, 2)
    

    И больше никаких проблем.


    С ним же связана и еще одна ошибка выполнения: Task is missing the start_date parameter, которая чаще всего говорит о том, что вы забыли привязать к оператору даг.


  • Всё на одной машине. Да, и базы (самого Airflow и нашей обмазки), и веб-сервер, и планировщик, и воркеры. И оно даже работало. Но со временем количество задач у сервисов росло, и когда PostgreSQL стал отдавать ответ по индексу за 20 с вместо 5 мс, мы его взяли и унесли.


  • LocalExecutor. Да, мы сидим на нём до сих пор, и мы уже подошли к краю пропасти. LocalExecutorа нам до сих пор хватало, но сейчас пришла пора расшириться минимум одним воркером, и придется поднапрячься, чтобы переехать на CeleryExecutor. А ввиду того, что с ним можно работать и на одной машиной, то ничего не останавливает от использования Celery даже не сервере, который естественно, никогда не пойдет в прод, чесслово!


  • Неиспользование встроенных средств:


    • Connections для хранения учетных данных сервисов,
    • SLA Misses для реагирования на таски, которые не отработали вовремя,
    • XCom для обмена метаданными (я сказал метаданными!) между тасками дага.

  • Злоупотребление почтой. Ну что тут сказать? Были настроены оповещения на все повторы упавших тасков. Теперь в моём рабочем Gmail >90k писем от Airflow, и веб-морда почты отказывается брать и удалять больше чем по 100 штук за раз.



Больше подводных камней: Apache Airflow Pitfails

Средства ещё большей автоматизации


Для того чтобы нам еще больше работать головой, а не руками, Airflow заготовила для нас вот что:


  • REST API он до сих пор имеет статус Experimental, что не мешает ему работать. С его помощью можно не только получать информацию о дагах и тасках, но остановить/запустить даг, создать DAG Run или пул.


  • CLI через командную строку доступны многие средства, которые не просто неудобны в обращении через WebUI, а вообще отсутствуют. Например:


    • backfill нужен для повторного запуска инстансов тасков.
      Например, пришли аналитики, говорят: А у вас, товарищ, ерунда в данных с 1 по 13 января! Чини-чини-чини-чини!. А ты такой хоба:
      airflow backfill -s '2020-01-01' -e '2020-01-13' orders
      
    • Обслуживание базы: initdb, resetdb, upgradedb, checkdb.
    • run, который позволяет запустить один инстанс таска, да еще и забить на всё зависимости. Более того, можно запустить его через LocalExecutor, даже если у вас Celery-кластер.
    • Примерно то же самое делает test, только еще и в баз ничего не пишет.
    • connections позволяет массово создавать подключения из шелла.

  • Python API довольно хардкорный способ взаимодействия, который предназначен для плагинов, а не копошения в нём ручёнками. Но кто ж нам помешает пойти в /home/airflow/dags, запустить ipython и начать беспредельничать? Можно, например, экспортировать все подключения таком кодом:


    from airflow import settingsfrom airflow.models import Connectionfields = 'conn_id conn_type host port schema login password extra'.split()session = settings.Session()for conn in session.query(Connection).order_by(Connection.conn_id):  d = {field: getattr(conn, field) for field in fields}  print(conn.conn_id, '=', d)
    

  • Подключение к базе метаданных Airflow. Писать в неё я не рекомендую, а вот доставать состояния тасков для различных специфических метрик можно значительно быстрее и проще, чем через любой из API.


    Скажем, далеко не все наши таски идемпотентны, а могут иногда падать и это нормально. Но несколько завалов это уже подозрительно, и надо бы проверить.


    Осторожно, SQL!
    WITH last_executions AS (SELECT    task_id,    dag_id,    execution_date,    state,        row_number()        OVER (            PARTITION BY task_id, dag_id            ORDER BY execution_date DESC) AS rnFROM public.task_instanceWHERE    execution_date > now() - INTERVAL '2' DAY),failed AS (    SELECT        task_id,        dag_id,        execution_date,        state,        CASE WHEN rn = row_number() OVER (            PARTITION BY task_id, dag_id            ORDER BY execution_date DESC)                 THEN TRUE END AS last_fail_seq    FROM last_executions    WHERE        state IN ('failed', 'up_for_retry'))SELECT    task_id,    dag_id,    count(last_fail_seq)                       AS unsuccessful,    count(CASE WHEN last_fail_seq        AND state = 'failed' THEN 1 END)       AS failed,    count(CASE WHEN last_fail_seq        AND state = 'up_for_retry' THEN 1 END) AS up_for_retryFROM failedGROUP BY    task_id,    dag_idHAVING    count(last_fail_seq) > 0
    



Ссылки


Ну и естественно первые десять ссылок из выдачи гугла содержимое папки Airflow из моих закладок.



И ссылки, задействованные в статье:


Подробнее..

Выгрузка данных из SAP через RFC на Python

16.02.2021 10:21:44 | Автор: admin

Поговорим о выгрузке данных из SAP ERP или S/4 HANA с использованием механизма SAP RFC.

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

Интерфейс SAP RFC (remote function call) позволяет вызывать различные функции SAP из стороннего приложения.

Преимущества этого интерфейса:

  • прямое и быстрое подключение с SAP.

  • возможность менять параметры запроса, запрашивая данные частями.

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

Установка

Для работы через RFC вам потребуется установить следующее:

  • Библиотека PyRFC https://github.com/SAP/PyRFC pip install pynwrfc

  • Библиотека SAP NW RFC для вашей платформы, скачанный с https://support.sap.com (нужен акаунт SAP).

  • Установить переменную окружения, указав каталог с библиотекой SAP NW RFC: SAPNWRFC_HOME=C:\NWRFC\nwrfcsdk\

Поиск уже имеющихся в системе функций

В системе SAP можно поискать уже готовые функциональные модули.

Сделать это можно следующим образом:

  1. Запустить транзакцию SE16 (просмотр таблиц).

  2. Указать имя таблицы TFDIR.

  3. Задать фильтры для поиска:

  • FUNCNAME=*MATERIAL* (задать маску поиска)

  • FMODE=R (возможность вызова функции через механизм RFC)

Чтение таблиц через RFC_READ_TABLE

RFC_READ_TABLE позволяет выгружать данные из таблицы SAP, ограничивая выборку фильтрами.

Несмотря на то, что SAP позиционирует эту функцию как тестовую и не предназначенную для использования в продуктивной среде, она вполне работоспособна.

Следует сказать, что RFC_READ_TABLE часто неудобна, т.к. она позволяет читать только одну таблицу (не поддерживает JOIN).

В этой статье я не буду подробно описывать работу с этой функцией, т.к. в интернете существует множество примеров ее использования. Вместо этого приведу пример вызова функции стандартной функции BAPI_MATERIAL_GETLIST, выдающий список материалов по заданным критериям поиска.

Учитывая недостатки RFC_READ_TABLE, а также, зачастую, отсутствие подходящих стандартных функций, оптимальным будет, чтобы разработчик ABAP написал необходимую функцию для выгрузки данных.

Просмотр функции через SE37

Входные и выходные параметры функции можно просмотреть с использованием транзакции SE37.

Параметры вызова функции присутствуют на следующих вкладках:

  • Importing - входные параметры простого типа (не табличные)

  • Exporting - выходные параметры простого типа

  • Tables - как входные, так и выходные параметры в виде таблиц

Рассмотрим использование SE37 на примере BAPI_MATERIAL_GETLIST.

Для того, чтобы посмотреть состав полей таблиц нужно дважды кликнуть на поле с типом данных.

SE37 - вкладка TablesSE37 - вкладка TablesПросмотр полей таблицыПросмотр полей таблицы

Эта функция выдает не слишком много полезных данных: Номер материала и описание.

Другие таблицы нужны для передачи на вход параметров выборки.

Например поиск по коду материала (MATNRSELECTION):

Таблица входных значенийТаблица входных значений

Подключаемся к SAP

Подключение к SAP с использованием библиотеки pyrfc делается не сложно, но нужно знать корректные параметры подключения, которые может сообщить специалист SAP basis.

Код на Python:

import pandas as pd

import os

import pyrfc

conn = pyrfc.Connection(user='', passwd='',

mshost='111.111.11.11',

msserv='3600',

sysid='010',

group='NN',

saprouter='',

lang='EN',

client='')

Вызываем необходимую функцию

Рассмотрим вызов функции на примере BAPI_MATERIAL_GETLIST.

Сначала зададим входные параметры.

В данном случае для каждого параметра в таблице будет по одной строке.

Строка таблицы задается как python dictionary, а вся таблица задается как list, состоящий из строк.

В нашем примере укажем фильтр на код материала: '' (т.е. все значения), а также укажем значение для Plant.

Для выборки используем SIGN="I" (Includes),

Варианты для OPTION:

  • EQ Equal

  • BT Between (требует задать значение для для LOW и HIGH)

  • LE Less Equal

  • GE Greater Equal

  • CP Contains Pattern

matnrselection = [{'SIGN':'I', 'OPTION':'CP', 'MATNR_LOW':''}]

plantselection = [{'SIGN':'I', 'OPTION':'EQ', 'PLANT_LOW':'NNNN'}]

Далее вызываем функцию с этими параметрами.

result = conn.call('BAPI_MATERIAL_GETLIST',

MATNRSELECTION = matnrselection,

PLANTSELECTION = plantselection)

Преобразуем результат в DataFrame

DataFrame можно получить в одну строку:

df = pd.DataFrame(result['MATNRLIST'])

Где MATNRLIST, это имя результирующей таблицы, указанное в разделе Tables.

Итак, с помощью буквально нескольких строк кода, мы получили данные из SAP в DataFrame и можем дальше работать с этими данным используя все возможности языка Python.

Подробнее..

SQL для девочек ( и не только)

06.07.2020 12:10:38 | Автор: admin
Для тех кто работает с данными в Excel зачастую встает проблема управления подключениями внешних таблиц к реляционным источникам. Да, Excel предоставляет здесь полный инструментарий, но не обеспечивает уровень комфорта и завышает планку требований к знаниям пользователей.

Для того что бы упростить процесс подключения к ODBC источникам и увеличить число пользователей хранилища был создан продукт, который являясь плагином к Ms Excel и он позволяет

1) Создавать excel таблицы с подключением к ODBC, основываясь на кастомном SQL запросе

2) Опираясь на (1) создавать pivot отчеты

3) Инструментарий по динамическому анализу данных

Keep-Only
Remove-Only
Undo
Redo

4) Автопостроитель SQL запросов с множественным объединением таблиц с одинаковым наименованием колонок

5) Навигатор по Excel книге

6) Обновление данных в DB из Excel

На данный момент поддерживаются следующие ODBC источники
1) MsSQl
2) Oracle
3) MySql
4) Postgres
5) Vertica



image

В ближайших планах добавить поддержку облака, первым в очереди стоит ClickHouse

Данный проект OpenSource, код и дистрибутивы доступны

https://sourceforge.net/projects/in2sql/

Замечания и предложения
https://t.me/in2sql

ER
Подробнее..
Категории: C , Postgresql , Sql , Excel , Oracle , Mysql , In2sql , Dwh

Мониторинг места в хранилищах

03.10.2020 12:04:36 | Автор: admin

Всем привет Хабровчане!!

Одной из проблем хранилищ данных, которая часто возникает в процессе работы - это постоянное увеличение их размеров. А добавление все новых и новых источников данных только ускоряет заполнение места на дисках.

Да, конечно же настройка чистки самых больших таблиц и периода историцируемости позволят сократить неконтролируемое увеличение места. Но если речь идет о хранилищах, которые бодро наполняются и добавляются всё новые "большие" таблицы, и количество их увеличивается то вопрос места в DWH всегда становится ребром. И возникает вопрос "А куда же ушло место?", "Что можно почистить?" или "Как обосновать руководству расширение хранилища?" Системы мониторинга на подобие ZABBIX позволяют только верхнеуровнево отследить увеличение дискового пространства на полке но не дают возможности отследить рост самих объектов в базе.

Сегодня хочу поделится своим маленьким лайфхаком как легко можно поставить на мониторинг размеры таблиц на примере MS SQL для дальнейшего анализа и оптимизации базы. Это маленькое решение которое может помочь сэкономить кучу времени чтобы проанализировать "Куда же ушло все место в хранилище?". Данный принцип можно применить и на других базах (Oracle, PostgreSQL и т.д.) с той лишь разницей, что названия системных таблиц будут другие.

Ниже описан небольшой план и набор скриптов MS SQL чтобы автоматизировать мониторинг места:

Это будет регламентное задание , которое собирает статистику ежедневно.

1) На первом шаге создаем таблицу для хранения истории и счетчик. В этой таблице будет сохранятся ежедневная история статистики для каждой таблицы.

CREATE SEQUENCE prm.sq_etl_log_1  AS bigint START WITH 1 INCREMENT BY 1  CREATE TABLE prm.dwh_size_of_tables(ddate date NULL,--Дата  на момент который смотрим статистику таблицыrun_id numeric(14, 0) NOT NULL,--ID Запуска сбора статистики, Счетчикdb_name varchar(20) NOT NULL,--База данныхschema_name sysname NOT NULL,--Схема таблицыtable_name sysname NOT NULL,--Название таблицыrow_count bigint NULL,--Количество строк в таблицеreserved_KB bigint NULL,--Ощий размер таблицы  вместе с индесамиdata_KB bigint NULL,--Размер самих данных в таблице index_size_KB bigint NULL,--Размер индексовunused_KB bigint NULL--неиспрользованное место) 

2) Далее необходимо создать процедуру которая будет ежедневно запускаться и собирать статистику по-таблично. Эту процедуру необходимо поставить на ежедневное задание для запуска. Она собирает срез размеров таблиц на текущий день.

Скрипт процедуры представлен ниже:

Скрипт процедуры
USE [LEMON]GOCREATE  PROCEDURE  [prm].[load_etl_log]ASdeclare @run_id intBEGIN--Если сегодня был запуск очищаем текущюую статистику и перезаливаемdelete from lemon.prm.dwh_size_of_tables where ddate = cast(getdate() as date);--Для страых периодов  храним только статистику только на начало и на середину месяцаdelete from  lemon.prm.dwh_size_of_tableswhere (DATEPART(day, ddate)not in (1,15) and ddate < dateadd(month ,-2, getdate())) DECLARE @SQL_text varchar(max),@SQL_text_final varchar(max); ;  set @SQL_text=   'USE {SCHEMA_FOR_REPLACE};insert into  lemon.prm.dwh_size_of_tablesSELECT cast(getdate() as date) date_time,'''+ convert(nvarchar , @run_id  ) +''' run_id ,''{SCHEMA_FOR_REPLACE}'' db_name,a3.name AS schema_name,--Схемаa2.name AS table_name,--Имя таблицыa1.rows AS row_count,--Число записей(a1.reserved + ISNULL(a4.reserved, 0)) * 8 AS reserved_KB,--Зарезервировано (КБ)a1.data * 8 AS data_KB,--Данные (КБ)(CASE WHEN (a1.used + ISNULL(a4.used, 0)) > a1.dataTHEN (a1.used + ISNULL(a4.used, 0)) - a1.dataELSE 0END) * 8 AS index_size_KB,--Индексы (КБ)(CASE WHEN (a1.reserved + ISNULL(a4.reserved, 0)) > a1.usedTHEN (a1.reserved + ISNULL(a4.reserved, 0)) - a1.usedELSE 0END) * 8 AS unused_KB --Не используется (КБ)FROM (SELECT ps.object_id,SUM(CASE WHEN (ps.index_id < 2)THEN row_countELSE 0END) AS [rows],SUM(ps.reserved_page_count) AS reserved,SUM(CASE WHEN (ps.index_id < 2)THEN (ps.in_row_data_page_count + ps.lob_used_page_count + ps.row_overflow_used_page_count)ELSE (ps.lob_used_page_count + ps.row_overflow_used_page_count)END) AS data,SUM(ps.used_page_count) AS usedFROM sys.dm_db_partition_stats psWHERE ps.object_id NOT IN (SELECT object_idFROM sys.tablesWHERE is_memory_optimized = 1)GROUP BY ps.object_id) AS a1LEFT OUTER JOIN (SELECT it.parent_id,SUM(ps.reserved_page_count) AS reserved,SUM(ps.used_page_count) AS usedFROM sys.dm_db_partition_stats psINNER JOIN sys.internal_tables it ON (it.object_id = ps.object_id)WHERE it.internal_type IN (202,204)GROUP BY it.parent_id) AS a4 ON (a4.parent_id = a1.object_id)INNER JOIN sys.all_objects a2 ON (a1.object_id = a2.object_id)INNER JOIN sys.schemas a3 ON (a2.schema_id = a3.schema_id)WHERE a2.type <> N''S''AND a2.type <> N''IT''';DECLARE @request_id nvarchar(36), @schema_for_replace nvarchar(100)DECLARE bki_cursor CURSOR FOR   SELECT name as schem    FROM    sys.databases--Здесь можно перечислить список баз по которым собираем статистику/*  where name  in ('DWH','DWH_copy','VN','VN_test') --and name ='DWH'*/OPEN bki_cursor  FETCH NEXT FROM bki_cursor INTO @schema_for_replaceWHILE @@FETCH_STATUS = 0  BEGINset @SQL_text_final = replace (@sql_text,'{SCHEMA_FOR_REPLACE}',@schema_for_replace);  execute (@SQL_text_final)FETCH NEXT FROM bki_cursor INTO @schema_for_replaceEND   CLOSE bki_cursor;  DEALLOCATE bki_cursor;END
Создать ежедневное задание

3) Теперь по мере наполнения таблицы dwh_size_of_tables можно смотреть статистику по-таблично и по базам. Для просмотра можно воспользоваться вот таким удобным скриптом ниже.

Статистика места в DWH по таблицам
--Статистика места  в DWH по таблицамselect top 10 ddate -- [Дата],run_id --,db_name --БД-,schema_name --Схема,table_name --Имя таблицы,row_count --Число записей,round(cast(reserved_KB as float) /1024/1024,2) as  reserved_GB --Зарезервировано (КБ),round(cast(data_KB as float) /1024/1024,2) as data_GB --Данные (КБ),round(cast(index_size_KB as float) /1024/1024,2) as index_size_GB --Индексы (КБ),round(cast(unused_KB as float) /1024/1024,2) as unused_GB--Не используется (КБ) from  lemon.prm.dwh_size_of_tableswhere ddate = cast(getdate() as date)-- and  db_name='DWH' order by reserved_GB desc
Статистика места в DWH по базам
--Статистика места  в DWH по  Базам select ddate -- [Дата],run_id --,db_name --БД-,round(cast(sum(reserved_KB) as float) /1024/1024,2) as  reserved_GB --Зарезервировано (КБ),round(cast(sum(data_KB) as float) /1024/1024,2) as data_GB --Данные (КБ),round(cast(sum(index_size_KB) as float) /1024/1024,2) as index_size_GB --Индексы (КБ),round(cast(sum(unused_KB) as float) /1024/1024,2) as unused_GB--Не используется (КБ),sum(row_count) row_count--Число записей from  lemon.prm.dwh_size_of_tableswhere ddate = cast(getdate() as date)-- and  db_name='DWH' group by   ddate,run_id,db_nameorder  by  ddate,run_id,sum(data_KB+index_size_KB) desc

4) Далее создаем еще 3 процедуры, которые позволят нам очень удобно просматривать историю по базам и по таблично. Эти процедуры используются не для сбора статистики а для показа этой статистики в красивом виде. Причем указав период за который хотим посмотреть статистику, она по-колоночно разбивает статистику.

Дневная статистика места по базам. Указываем период за который смотрим
USE [LEMON]GO/****** Object:  StoredProcedure [prm].[dwh_daily_size_statistics]    Script Date: 02.09.2020 18:35:02 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE  procedure [prm].[dwh_daily_size_statistics]   @sdate date, @edate dateASBEGIN--Собираем подневную статистикуdeclare   @str nvarchar(4000)set @str= stuff (  ( select  N','+ 'round(cast(sum(case when ddate =  cast('''+ cast( ddate as nvarchar)+'''as date)  thenreserved_KBend) as float) /1024/1024,0)  ['+ cast( ddate as nvarchar)+']'+char(10)from ( select distinct ddate from  lemon.prm.dwh_size_of_tableswhere ddate >=@sdate and  ddate<@edate) t order by t.ddate   for xml path('')  ,type  ).value('.','nvarchar(max)'),  1,0,'' )-- column_string--print @strexec (' select db_name --БД-'+@str+' from  lemon.prm.dwh_size_of_tables--where ddate = cast(getdate() as date) group by  db_name--order  by  db_name');end ;GO
Месячная статистика места по базам. Указываем период просмотра истории.
USE [LEMON]GO/****** Object:  StoredProcedure [prm].[dwh_monthly_size_statistics]    Script Date: 02.09.2020 18:35:09 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE procedure [prm].[dwh_monthly_size_statistics]   @sdate date, @edate dateASbegin --Собираем помесячую статистикуdeclare   @str2 nvarchar(4000)set @str2= stuff (  ( select  N','+ 'round(cast(sum(case when ddate =  cast('''+ cast( ddate as nvarchar)+'''as date)  thenreserved_KBend) as float) /1024/1024,0)  ['+ CAST(year( ddate) as nvarchar) +'_'+ CAST(month( ddate) as nvarchar)--cast( ddate as nvarchar)+']'+char(10)from ( select distinct ddate from  lemon.prm.dwh_size_of_tableswhere ddate >=@sdate and  ddate<@edate and day(ddate)=1) t order by t.ddate   for xml path('')  ,type  ).value('.','nvarchar(max)'),  1,0,'' )exec (' select db_name --БД---,table_name'+@str2+' from  lemon.prm.dwh_size_of_tables--where ddate = cast(getdate() as date) group by  db_name--,table_nameorder  by  db_name');end;GO
Процедура для просмотра истории размеров таблиц
USE [LEMON]GO/****** Object:  StoredProcedure [prm].[dwh_monthly_table_size_statistics]    Script Date: 02.09.2020 18:36:15 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER procedure [prm].[dwh_monthly_table_size_statistics]   @sdate date, @edate date ,@db_name nvarchar(100)ASbegin --Собираем помесячую статистикуdeclare   @str2 nvarchar(4000)set @str2= stuff (  ( select  N','+ 'round(cast(sum(case when ddate =  cast('''+ cast( ddate as nvarchar)+'''as date)  thenreserved_KBend) as float) /1024/1024,0)  ['+ CAST(year( ddate) as nvarchar) +'_'+ CAST(month( ddate) as nvarchar)--cast( ddate as nvarchar)+']'+char(10)from ( select distinct ddate from  lemon.prm.dwh_size_of_tableswhere ddate >=@sdate and  ddate<@edate and day(ddate)=1   ) t order by t.ddate   for xml path('')  ,type  ).value('.','nvarchar(max)'),  1,0,'' ) declare @ORDER_DATE NVARCHAR(100) SET @ORDER_DATE= convert(nvarchar, year( @edate)  ) +'_'+  convert(nvarchar, month( @edate) ) SELECT  @ORDER_DATE = convert(nvarchar, year( DDATE)  ) +'_'+  convert(nvarchar, month( DDATE) ) FROM (select MAX( ddate ) DDATE from  lemon.prm.dwh_size_of_tableswhere ddate >=@sdate and  ddate<@edate and day(ddate)=1 ) tt  ;declare @ddb_name nvarchar(100)set @ddb_name =  case when @db_name is null then '' else  ' and '+ 'db_name= '''+@db_name + '''' end exec (' select db_name --БД-,table_name'+@str2+' from  lemon.prm.dwh_size_of_tableswhere 1=1  ' + @ddb_name  + '-- ddate = cast(getdate() as date) group by  db_name,table_name order by  db_name,['+ @ORDER_DATE +'] desc');end;

5) В итоге у нас получились 3 процедуры которые позволяют :

A) Смотреть историю увеличения/уменьшения БД подневно

B) Смотреть историю увеличения/уменьшения БД помесячно

C) Смотреть историю увеличения/уменьшения таблиц помесячно. Очень удобно когда нужно отследить по конкретной таблице когда по ней пошел рост.

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

--Дневная статистика места по базам указываем период  за который смотримexec  LEMON.prm.dwh_daily_size_statistics @sdate ='2020-08-01', @edate ='2020-09-01'--Месячная статистика места по базам указываем период  за который смотримexec  LEMON.prm.dwh_monthly_size_statistics @sdate ='2020-03-01', @edate ='2020-09-01'--Месячная статистика места по каждой таблицеexec  LEMON.prm.dwh_monthly_table_size_statistics   @sdate ='2020-02-01', @edate ='2020-08-01', @db_name ='DWH'--если указываем null то показывает все таблицы по всем базам

Как видно на картинке выше по ней очень удобно просмотреть какая база начала резко увеличиваться в размерах. Более того этими тремя процедурами очень быстро можно найти , например, таблицу или базу которая начала в какой-то момент сильно расти. Особенно удобно когда в хранилище уже созданы тысячи объектов, и ручной поиск уже не применим.

Вывод: Настроив небольшой такой функционал по мониторингу места можно очень сильно упростить жизнь в будущем, в части касающейся роста базы и поиска объектов в хранилище, которые сильно выросли. Более того, это поможет определить по каким проектам или системам наблюдается рост размера хранилища и легко обосновать руководству, например, необходимость дополнительного места или настроить чистку таблиц, по которым наблюдается быстрый рост.

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

P.S. Все скрипты выложены на GitHub по ссылке ниже:

https://github.com/michailo87/MSSQL

До скорых встреч !!

Подробнее..

Business Intelligence на больших данных наш опыт интеграции

20.01.2021 14:20:49 | Автор: admin

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

Если вы читаете наш блог, то уже знаете о платформе Visiology хотя бы в общих чертах (если нет, это можно легко исправить, прочитав наш первый пост). Но сегодня речь пойдет не только о платформе Visiology и BI как таковых, но также о наших новых друзьях Arenadata. А точнее, об интеграции, которая позволила реализовать гибридную работу аналитики с большой скоростью и на больших объемах данных.

Зачем потребовалась интеграция Arenadata и Visiology?

Подходов к работе BI-систем на сегодняшний день несколько. Но когда речь идет о больших данных для самых разных задач, обычно используется ROLAP. Работает он достаточно просто: когда пользователь нажимает что-то на дашборде, например, выбирает какой-то фильтр, внутри платформы формируется SQL-запрос, который уходит на тот или иной бэкэнд. В принципе, под системой BI может лежать любая СУБД, которая поддерживает запросы от Postgres до Teradata. Подробнее о схемах работы OLAP я рассказывал здесь.

Преимущество интеграции BI с СУБД заключается в том, что для работы системы, по сути, нет ограничения по объему данных. Но при этом падает скорость выполнения запросов - конечно, если не использовать специализированную колоночную СУБД, например, ClickHouse или Vertica. И, хотя у ClickHouse спектр возможностей пока еще уже, чем у той же Vertica, система развивается и выглядит очень многообещающей.

Но даже с колоночной СУБД есть свои минусы при работе с BI, и самый первый из них это более низкая эффективность использования кэша на уровне платформы в целом, потому что СУБД, в отличие от самой BI-платформы, "не знает" многого о поведении пользователей и не может использовать эту информацию для оптимизации. Когда большое количество пользователей начинают работать, по-разному делать запросы и обращаться к дашбордам, требования к железу, на котором крутится СУБД даже хорошая, аналитическая и колоночная могут оказаться очень серьезными.

Второй момент это ограничение аналитической функциональности: все, что не укладывается в SQL-запрос, поддерживаемый распределенной СУБД, отсекается автоматически (например, в случае ClickHouse - это оконные функции). И это проблема, потому что в BI есть много вещей, которые с трудом транслируются в SQL-запросы или выполняются неоптимально.

Второй вариант это In-memory OLAP. Он подразумевает перенос всех обрабатываемых данных в специальный движок, который молниеносно прорабатывает базу в 200-300 Гб это порядок единицы миллиардов записей. Кстати, подробнее про ограничения In-Memory OLAP я уже рассказывал здесь. На практике встречаются инсталляции In-Memory OLAP, укомплектованные 1-2-3 терабайтами оперативной памяти, но это скорее экзотика, причем дорогостоящая.

Практика показывает, что далеко не всегда можно обойтись тем или иным подходом. Когда требуются одновременно гибкость, возможность работы с большим объемом данных и поддержка значительного количества пользователей, возникает потребность в гибридной системе, которая с одной стороны загружает данные в движок In-Memory OLAP, а с другой постоянно подтягивает нужные записи из СУБД. В этом случае движок OLAP используется для доступа ко всему массиву данных, без всяких задержек. И в отличие от чистого In-Memory OLAP, который нужно периодически перезагружать, в гибридной модели мы всегда получаем актуальные данные.

Такое разделение данных на горячие и холодные объединяет плюсы обоих подходов ROLAP и In-Memory, но усложняет проект внедрения BI. Например, разделение данных происходит вручную, на уровне ETL процедур. Поэтому для эффективной работы всего комплекса очень важна совместимость между бэкэндом и самой BI-системой. При том, что SQL-запросы остаются стандартными, в реальности всегда есть аспекты их выполнения, нюансы производительности.

Arenadata и Arenadata QuickMarts

Платформа данных Arenadata состоит из нескольких компонентов, построенных на базе открытых технологий, и используется многими российскими и зарубежными компаниями. В состав решения входит собственное MPP решение на базе Greenplum, дистрибутив Hadoop для хранения и обработки неструктурированных и слабоструктурированных данных, система централизованного управления ADCM (Сluster Management) на базе Ansible и другие полезные компоненты, в том числе Arenadata QuickMarts (ADQM).

СУБД ADQM это колоночная СУБД от Arenadata, построенная на базе ClickHouse, аналитической СУБД, которую развивает Яндекс. Изначально ClickHouse создавалась для внутреннего проекта Яндекс.Метрика, но эта СУБД очень понравилась сообществу. В результате исходный код ClickHouse был переведен в OpenSource (лицензия Apache-2) и стал популярен по всему миру. На сегодняшний день насчитывается порядка 1000 инсталляций ClickHouse по всему миру, и только 1/3 из них в России. И хотя Яндекс остается основным контрибьютором развития СУБД, лицензия Apache-2 позволяет абсолютно свободно использовать продукт и вносить изменения в проект.

Современная колоночная СУБД использует аппаратную оптимизацию CPU (SSE). ClickHouse может очень быстро выполнять запросы за счет векторных оптимизаций и утилизации всего ресурса многоядерных CPU. На базе ClickHouse работают огромные кластера сам Яндекс растягивает эту СУБД на несколько сотен серверов. Это гарантирует, что вместе с этим решением вы можете масштабироваться в достаточно больших объемах.

Но главная фича ClickHouse в нашем контексте это эффективная работа с достаточно специфическими аналитическими запросами. Если витрины уже отстроены и вам нужно предоставить доступ пользователей к BI с минимальной латентностью, эта история как раз для ClickHouse. Эта СУБД прекрасно справляется с запросами без джойнов и соединений.

Во многих сравнениях ClickHouse дает серьезную фору даже классическим СУБД, например, той же Oracle Exadata. Результаты этих тестов можно найти на ресурсах Яндекса.

Производительность QuickMarts

  • Типичные запросы быстрей чем за секунду

  • > 100 раз быстрей чем Hadoop и обычные СУБД

  • 100 млн - 1 миллиард строк в секунду на одной ноде

  • До 2 терабайт в секунду для кластера на 400 нод

Но вернемся к Arenadata QuickMarts. Это сборка ClickHouse, которая немного отличается от сборки Яндекса. Наши коллеги из Arenadata даже позже выпускают релизы, потому что проводят больше тестов, чтобы серьезные задачи в продакшене работали только на стабильных версиях.

При этом установка и настройка ADQM происходит из Arenadata Cluster Manager. Кастомизированная СУБД обладает расширенными механизмами авторизации пользователей, a также средствами мониторинга на базе Graphite и Grafana. Но самое главное, что QuickMarts изначально располагает готовыми коннекторами и прозрачно взаимодействует с другими компонентами платформы, в т.ч. с ADB (Greenplum), что позволяет по мере необходимости подгружать данные из ADB в ADQM.

В нашем случае QuickMarts используется для работы с витринами, к которым через BI обращаются сотни или тысячи пользователей. Архитектура системы позволяет выдать им данные здесь и сейчас, а не ждать 20-30 секунд, когда обработается их запрос по витринам в более медленной СУБД.

Как работает интеграция Arenadata и Visiology

Когда Visiology используется вместе с Arenadata, схема работы системы выглядит следующим образом. Основное хранилище данных может быть реализовано на базе ADB (GreenPlum), из которой создаются витрины данных, хранящиеся уже в ADQM. За счет интеграции между компонентами решения система работает как единое целое, а необходимые для запросов данные поднимаются на нужный уровень автоматически.

Фактически в аналитической системе создается только один дашборд, а графику обрабатывает движок In-Memory ViQube ядро платформы Visiology. Пользователь лишь выбирает те или иные фильтры, а задача по выгрузке самих транзакций выполняется уже на бэкенде ресурсами QuickMarts.

Раньше подобная интеграция была только с Vertica, но сейчас мы совместно с коллегами сделали интеграцию для Arenadata QuickMarts. Это радостная новость для сторонников ClickHouse, потому что BI работает с популярной СУБД по гибридной схеме. При этом Arenadata DB, выполняющая функцию корпоративного хранилища данных, обеспечивает необходимую трансформацию данных для дальнейшей работы QuickMarts и Visiology.

Все запросы BI обрабатываются движком ViQube. Если пользователь обращается к тем данным, которых нет в памяти, система автоматически генерирует SQL-запрос, который выполняется на Arenadata QuickMarts.

Чтобы все это заработало, мы реализовали поддержку диалекта ClickHouse для основных аналитических функций и добавили автоматическое переключение между режимами работы OLAP в зависимости от того, где находятся данные на самом деле. Однако для пользователя все остается предельно прозрачным: он даже не знает, как работает система просто делает запросы в интерфейсе BI и получает результаты, причем достаточно быстро.

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

Развиваемся дальше

Сейчас интеграция находится на стадии версии v1.0, и мы планируем дальнейшие доработки. В частности, уже сейчас речь идет о том, чтобы расширить набор доступных аналитических возможностей, а также об интеграции в единую консоль управления (например, у Arenadata есть решение Cluster Manager (ADCM), которое позволяет управлять всеми компонентами ландшафта из единой консоли, рассматриваем это как один из вариантов).

Также на повестке дня остро стоит задача автоматизации настройки метаданных. Сейчас их нужно размечать в специальном интерфейсе - это довольно трудоемко. Хочется, чтобы система самостоятельно распознавала бы все необходимые параметры, получив путь к той или иной витрине.

В целом, мы остались очень довольны и сотрудничеством с Arenadata, и той интеграцией с ClickHouse и ADQM, которая получилась. Теперь в аналитической платформе Visiology можно одновременно работать с источниками данных любого масштаба - от Small Data (ручной ввод, Excel) до Big Data (миллиардов или даже сотни миллиардов транзакций из распределенных хранилищ данных). А гибридный режим работы, который мы реализовали вместе с Arenadata, еще и позволяет сделать это с разумными затратами на оборудование.

Будем признательны, если вы напишете в комментариях, с какими сценариями запуска BI на больших данных вы сталкивались. Ну а мы всегда готовы поделиться своим опытом в деталях!

Подробнее..

Как QA в управлении хранилища данных эволюционировал. Часть 2

19.03.2021 18:12:20 | Автор: admin

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

Итак, поехали!

Проблемы прошлого

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

  • Ручной сбор пакета разработчиками.

  • Ручное ревью пакета.

  • Необходимость в синхронизации продуктового и тестового контуров.

  • Недоступность операций над метаинформацией при возникновении очереди.

  • Отдельный контур для тестирования интеграции.

Кроме того, из последней проблемы вытекает тот факт, что целых три тестовых контура используются в текущем flow.

Три тестовых контура (vial, live и test)

Я уже писал про vial и live серверы для проведения модульного и регрессионного тестирования ETL-процессов. Они позволили отделить эти виды тестов, но старый test-контур при этом никуда не делся и использовался для интеграционного тестирования пакета, уже прошедшего модульное тестирование на vial. Кроме того, ряд задач невозможно было протестировать на vial в силу различных обстоятельств, и с ними по-прежнему работали на test.

Было: все тестирование на одном контуре.

Стало: модульное и интеграционное тестирование, а также регресс разнесены по разным контурам.

Распределение этапов тестирования между контурамиРаспределение этапов тестирования между контурами

Таким образом:

  • тестовых контуров стало больше;

  • test по-прежнему использовался.

Зачем нужен test?

Во-первых, test был нужен для проведения интеграционного тестирования. Успешное прохождение модульного тестирования не означало, что пакет полностью корректен и никак не сломает остальные объекты хранилища. Например, разработчик мог, скажем, поправить длину какого-то поля в таблице и в метаинформации, и на этапе модульного тестирования это никак бы не отразилось, а зависимый ETL-процесс мог быть завязан на старое значение и при изменениях падал. Это можно отловить только при интеграционном тестировании.

Во-вторых, ряд задач просто нельзя было на тот момент тестировать на vial по разным причинам. Например, бэкапы для некоторых таблиц нельзя (или очень проблематично) было перенести на vial с prod. Поэтому их тестировали на test.

Хорошо, с важностью и необходимостью testа разобрались.

А в чем были проблемы с test при текущем flow с тремя тестовыми контурами?

Оказывается, проблем было несколько:

  • необходимость синхронизаций с продом;

  • конфликты задач;

  • деградация данных.

О синхронизации уже говорили, поэтому рассмотрим две оставшиеся проблемы.

При совместном использовании теста многими QA-инженерами время от времени возникали конфликты метаинформации и/или физических данных из-за одновременного использования в разных задачах одинаковых объектов. В этом случае нужен был откат задач и согласование порядка работы с объектами (порядка наката и тестирования).

При этом могла возникнуть ситуация, что из-за нехватки места на тестовом контуре или из-за ошибки были удалены бэкапы по задаче. В этом случае тестовое окружение на test-контуре было сломано, и все исправить могла только синхронизация. То есть мы получали много рисков и проблем.

Давайте рассмотрим решения проблем при помощи наших разработок, и начнем с проблемы ручного сбора пакетов.

Текущие реалии

НастоящееНастоящее

Мы шаг за шагом улучшали наши процессы и создавали новые инструменты, развивая их. Постепенно переходили к автоматизации. Остановимся на этом подробнее.

Составляющие автоматизацииСоставляющие автоматизации

Про авторелиз уже рассказали в предыдущей статье, поэтому рассмотрим остальные составляющие.

Портал автоматизации

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

Через некоторое время в работу был введен портал автоматизации это наше внутреннее веб-приложение, которое помогает разработчикам, QA-инженерам, ревьюверам и ребятам, выполняющим релизы задач, вести свою работу с пакетами в одном месте.

Отображение пакета на портале автоматизацииОтображение пакета на портале автоматизации

Какие функции предоставляет портал автоматизации?

  • Автоматический сбор пакета.

  • Накаты задач на все контуры.

  • Большой пул работ с метаинформацией.

  • Управление Демонами.

  • Ревью.

  • Управление релизом.

На портале автоматизации у разработчика появилась возможность собрать свой пакет автоматически. Причем, если задача не содержит специфических объектов и действий, на лету создается пакет с типовым содержимым и сценарием. А если сценарий по данной задаче содержит специфические действия, то на портале есть функционал создания сложного сценария работы с объектами хранилища данных и наката.

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

Автотестер

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

Автотестер запускает накаты задач ежедневно в 20:30, поскольку в это время нагрузка на тестовый контур резко снижается и работы автотестера никак не будут блокировать работу сотрудников. Он берет все задачи, которые успешно прошли ревью и по которым в Jira указан уровень тестирования автоматическое. Такой уровень тестирования проставляется у всех задач, для которых не предполагаются ручные действия во время наката. А далее запускает процесс создания пробирок.

Автотестер создает чат с разработчиком и QA и в случае возникновения ошибок окружения (моргнула база, кончилось место или же в самом пакете вылезли ошибки, которые не были выявлены на ревью) отправляет туда лог ошибки.

Пример slack-канала и сообщения об ошибке в немПример slack-канала и сообщения об ошибке в нем

После успешного переноса задачи на vial запускаются автотесты и сравнения текущих и предыдущих версий целевых таблиц, а также чекеры целостности данных. Результаты всех этих проверок автотестер отправляет в тот же slack-канал.

Результаты автопроверок в slack-каналеРезультаты автопроверок в slack-канале

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

Авторевью

Давайте вспомним, какие проблемы были при проведении ревью вручную:

  • занимает много времени;

  • невозможно отследить глазами выполнение абсолютно всех требований.

Меню запуска ревью на портале автоматизацииМеню запуска ревью на портале автоматизации

Большое количество проверок было просто вынесено в отдельный сервис, который запускается для проверки созданного пакета и выдает свой вердикт о его качестве. Далее ревьюверу остается проверить логи с результатами авторевью и самые критичные требования к объектам из задачи. То есть ревью стало двухфакторным и проходит гораздо быстрее.

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

Рассмотрим основные категории проверок в рамках ревью.

Meta-review

Работа с метаданными объектов хранилища осуществляется в SAS Data Integration Studio. Метаданные физически хранятся на SAS-сервере и представляют собой таблицы атрибутов и таблицу связей их используем для автоматизации. После внесения разработчиком изменений в метаданные, происходит запрос на SAS-сервер по объектам, которые были затронуты доработкой.

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

Package-review

Результат разработки опубликованный в VCS релиз-пакет, который содержит в себе все файлы, необходимые для установки задачи, а также файлы, свидетельствующие о корректности разработки.

Package-review запускается для проверки наполнения релиз-пакета. Проверяется наличие всех необходимых файлов, соответствие конфига и содержания пакета, корректность создания или изменения физической структуры объектов, производится парсинг скриптов, сопоставление объектов, дорабатываемых в скриптах, с объектами метаданных. В результате работы package-review пользователь получает отчет со списком объектов релиз-пакета, не прошедших проверки, и рекомендации по устранению проблем.

Diff-review

Запускает python-скрипт, выполняющий сравнение деплоев джобов до и после разработки и создающий diff-файлы.

Log-review

Выполняет проверку логов в пакете.

Автотесты

На крупных проектах в целях автоматизации тестирования пишутся собственные тестовые фреймворки, и наш проект не исключение. На связке python + pytest создан наш тестовый фреймворк, позволяющий:

  • Запускать автотесты на всех объектах по задаче.

  • Выполнять часть тестовых проверок на лету, запуская самописные тестовые функции.

  • Формировать итоговый отчет с результатами тестирования в Allure.

У запуска автотестов есть особенность: их перечень зависит от объектов тестовой задачи. Например, при создании нескольких абсолютно новых ETL-процессов интеграционные проверки запускаться не будут, поскольку в хранилище еще нет никаких зависимостей от этих процессов. И наоборот: при доработке существующих процессов будут запускаться интеграционные проверки.

Какие виды тестов во фреймворке существуют?

  • Static включают проверки хардкода, корректности метаинформации и настроек инкрементальной загрузки ETL-процесса.

  • BI проверяют зависимости в SAP BO юниверсах.

  • Интеграционные тесты проверяют возможные ошибки в зависимых ETL-процессах хранилища и в важных отчетах.

  • Work проверяют что корректно отработала инкрементальная загрузка новых/измененных данных из источников, обновились данные и так далее.

Интеграционное тестирование

В этом направлении мы совершили огромный прорыв и очень им гордимся.

Итак, интеграционное тестирование прошло следующие этапы:

  • Ручной запуск всех зависимых процессов на тестовом контуре.

  • Вынесение части проверок в авточекер и выполнение их автоматически.

  • Расширение перечня автопроверок и переход к накату на тест только метаинформации.

Апогеем этой эволюционной лестницы стал полный отказ от интеграционного контура. Почему это удалось?

  • Вместо инстанса тестового контура для подтягивания всех зависимостей стали использовать специальную платформу MG (наша внутренняя разработка), содержащую метаданные хранилища, а также описывающую логическую и концептуальную модели хранилища.

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

Так мы победили ненавистные синхронизации и избавились от теста, тем самым чуть разгрузили тестовый контур (привет нашим DB-админам и спасибо им за терпение), сделали тестовое окружение более доступным нет больше потерянных дней тестирования из-за проблем с синхронизацией.

Попутно были достигнуты следующие результаты:

  • Влияние человеческого фактора на итоги интеграционного тестирования уменьшилось в разы.

  • Время интеграционного тестирования сократилось на 8090%.

  • Качество самих интеграционных проверок улучшилось за счет максимально актуальных данных.

Качество проверок улучшилось, потому что на тесте не всегда были актуальные данные, да и зависимости в BI и важных стратегических отчетах ранее мы не могли проверить, а с приходом автоматизации это стало реальным.

Выполнение проверок на лету

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

Какие функции у нас есть?

  • ddl(имя_таблицы) возвращает DDL-скрипт создания таблицы, используемый для проверки корректности метаинформации и соответствия ее ТЗ.

  • profile(имя_таблицы) сводный отчет наполняемости таблицы (насколько заполнено каждое поле таблицы, какие уникальные значения есть в различных полях и т. д.).

  • dq_check(имя_таблицы, ключ) позволяет определить, сколько дублей и NULL есть в ключевых полях таблицы, а также выявить проблемы версионности.

  • compare_(таблица1, таблица2, ключ) самый основной инструмент, выдающий результаты сравнения двух таблиц.

Compare() показывает, сколько столбцов и строк в каждой таблице, сколько строк сджойнилось и в каких столбцах были обнаружены расхождения для сджойненных записей.

Ниже показано, что все записи из первой и второй таблицы (12987767 234 строки) сджойнились, но по полю order_id были обнаружены расхождения в 9458 234 строках.

Пример результата функции compare()Пример результата функции compare()

Если такое расхождение и предполагалось получить в результате доработки ETL-процесса все хорошо, если же оно неожиданно это повод для обсуждения с аналитиком и разработчиком.

При помощи функций compare() выполняются сравнения с прототипом и бэкапом. Они позволяют сравнить: полностью все таблицы, только актуальные версии записей или же отдельные партиции (для больших партицированных таблиц).

Тест-кейсы в Allure

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

Пополнение базы автотестов

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

Разработка собственных допсервисов

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

Разработка

Ранее в статье я упомянул проблему блокировки всех пользователей при выполнении операций с метаинформацией. Все такие операции выстраивались в очередь, и пока очередь не опустеет пользователи с метой работать не смогут.

Эта проблема затрагивала не только QA-инженеров, но и самих разработчиков. И для ее решения было предложено использовать отдельные метасерверы. На них с помощью плейбуков разработчики готовят окружение, разрабатывают свои задачи и проводят первичную проверку результата.

Далее задача отправляется согласно установленному flow.

Проблемы на данном этапе

Из оставшихся проблем были решены:

  • Необходимость в синхронизации продуктового и тестового контуров отпала после перехода на vial и автоматизацию интеграционного тестирования.

  • Ручное ревью пакета решено после введения авторевью. Полностью избавиться от ручного ревью не получится, но механизм авторевью значительно разгрузил сотрудников.

  • Имеется целых три тестовых контура теперь для тестирования используется только связка vial/live, причем live лишь для некоторых задач.

  • Для тестирования интеграции требуется отдельный контур контур больше не используется.

  • Недоступность операций с метаинформацией при возникновении очереди (изначально выделена не была, но по ходу статьи упоминалась) к сожалению, пока не решена.

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

Но разработчики уже решили эту проблему в своем окружении, а в планах решить ее и применительно к QA.

Дорога к светлому будущему

Мы уже многое сделали для улучшения нашей работы, но нам еще многое предстоит.

Будущее прекрасное далекоБудущее прекрасное далеко

Какие же наши основные задачи?

  • Переход авторевью в разработку и поддержку на стороне QA.

  • Тестовые контуры переходят в зону ответственности QA в команду QA нанимается SRE.

  • Тестирование на отдельных метасерверах.

  • Разработка новых сервисов автоматизации.

Тестирование на отдельных метасерверах позволит избавить от зависаний меты на vial, но не все сразу. Впрочем, мы обязательно это сделаем.

А что касается новых сервисов автоматизации, то сейчас мы занимаемся написанием статистического анализатора результата регресса, анализатора корректности эталонного SQL-кода (этот код поддерживает в актуальном состоянии системный аналитик), на основе которого разработчики создают ETL-процессы, а также автоматизацией тестирования инкрементальной загрузки.

Заключение

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

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

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

Слаженная работа команд разработки и тестирования в DWH позволила уверенно двигаться в сторону автоматизации, и со временем процессы внутри управления кардинально поменялись.

Вот что было сделано:

  • Автоматизировано большое количество действий, выполняющихся ранее вручную: сбор пакетов, накат/откат задач, ревью, большая часть тестов и т. д.

  • Разработаны внутренние сервисы для ускорения и повышения эффективности работы с хранилищем:портал автоматизации, авторевью, автотесты и т. д.

  • Процессы непрерывно улучшаются за счет пополнения базы тестов, повышения стабильность автотестов.

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

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

Спасибо, заходите почитать про наше хранилище!

Подробнее..

Применение low-code в аналитических платформах

24.09.2020 18:11:15 | Автор: admin
Уважаемые читатели, доброго дня!

Задача построения программных платформ для накопления и проведения аналитики над данными рано или поздно возникает у любой компании, в основе бизнеса которой заложена интеллектуально нагруженная модель оказания услуг или создания технически сложно изготавливаемых продуктов. Построение аналитических платформ сложная и трудозатратная задача. Однако любую задачу можно упростить. В этой статье я хочу поделиться опытом применения low-code-инструментов, помогающих в создании аналитических решений. Данный опыт был приобретён при реализации ряда проектов направления Big Data Solutions компании Неофлекс. Направление Big Data Solutions компании Неофлекс с 2005 года занимается вопросами построения хранилищ и озёр данных, решает задачи оптимизации скорости обработки информации и работает над методологией управления качеством данных.



Избежать осознанного накопления слабо и/или сильно структурированных данных не удастся никому. Пожалуй, даже если речь будет идти о малом бизнесе. Ведь при масштабировании бизнеса перспективный предприниматель столкнётся с вопросами разработки программы лояльности, захочет провести анализ эффективности точек продаж, подумает о таргетированной рекламе, озадачится спросом на сопроводительную продукцию. В первом приближении задача может быть решена на коленке. Но при росте бизнеса приход к аналитической платформе все же неизбежен.

Однако в каком случае задачи аналитики данных могут перерасти в задачи класса Rocket Science? Пожалуй, в тот момент, когда речь идёт о действительно больших данных.
Чтобы упростить задачу Rocket Science, можно есть слона по частям.



Чем большая дискретность и автономность будет у ваших приложений/сервисов/микросервисов, тем проще вам, вашим коллегам и всему бизнесу будет переваривать слона.

К этому постулату пришли практически все наши клиенты, перестроив ландшафт, основываясь на инженерных практиках DevOps-команд.

Но даже при раздельной, слоновьей диете мы имеем неплохие шансы на перенасыщение IT-ландшафта. В этот момент стоит остановиться, выдохнуть и посмотреть в сторону low-code engineering platform.

Многих разработчиков пугает перспектива появления тупика в карьере при уходе от непосредственного написания кода в сторону перетаскивания стрелочек в UI-интерфейсах low-code систем. Но появление станков не привело к исчезновению инженеров, а вывело их работу на новый уровень!

Давайте разбираться почему.

Анализ данных таких сфер, как: логистика, телеком-индустрия, медиаисследования, финансовый сектора бизнеса, всегда сопряжён со следующими вопросами:

  • Скорость проведения автоматизированного анализа;
  • Возможность проведения экспериментов без воздействия на основной поток производства данных;
  • Достоверность подготовленных данных;
  • Отслеживание изменений и версионирование;
  • Data proveance, Data lineage, CDC;
  • Быстрота доставки новых фич на продукционное окружение;
  • И пресловутое: стоимость разработки и поддержки.

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

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

Давайте проведём аналогию с низкоуровневыми и высокоуровневыми языками программирования. Переход от низкоуровневых языков в сторону высокоуровневых это переход от написания прямых директив на языке железа в сторону директив на языке людей. То есть добавление некоторого слоя абстракции. В таком случае переход на low-code-платформы с высокоуровневых языков программирования это переход от директив на языке людей в сторону директив на языке бизнеса. Если найдутся разработчики, которых этот факт опечалит, тогда опечалены они, возможно, ещё с того момента, как на свет появился Java Script, в котором используются функции сортировки массива. И эти функции, разумеется, имеют под капотом программную имплементацию другими средствами того же самого высокоуровнего программирования.

Следовательно, low-code это всего лишь появление ещё одного уровня абстракции.

Прикладной опыт использования low-code


Тема low-code достаточно широка, но сейчас я хотел бы рассказать о прикладном применении малокодовых концепций на примере одного из наших проектов.

Подразделение Big Data Solutions компании Неофлекс в большей степени специализируется на финансовом секторе бизнеса, cтроя хранилища и озёра данных и автоматизируя различную отчётность. В данной нише применение low-code давно стало стандартом. Среди прочих low-code-инструментов можно упомянуть средства для организации ETL-процессов: Informatica Power Center, IBM Datastage, Pentaho Data Integration. Или же Oracle Apex, выступающий средой быстрой разработки интерфейсов доступа и редактирования данных. Однако применение малокодовых средств разработки не всегда сопряжено с построением узконаправленных приложений на коммерческом стеке технологий с явно выраженной зависимостью от вендора.

С помощью low-code-платформ можно также организовывать оркестрацию потоков данных, создать data-science-площадки или, например, модули проверки качества данных.

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



Медиаисследования технологически нагруженная сфера бизнеса. Распознавание видеоряда, сбор данных с устройств, анализирующих просмотр, измерение активности на веб-ресурсах всё это подразумевает наличие у компании большого IT-штата и колоссального опыта в построении аналитических решений. Но экспоненциальный рост количества информации, числа и разнообразия ее источников заставляет постоянно прогрессировать IT-индустрию данных. Самым простым решением масштабирования уже функционирующей аналитической платформы Mediascope могло стать увеличение штата IT. Но гораздо более эффективное решение это ускорение процесса разработки. Одним из шагов, ведущих в эту сторону, может являться применение low-code-платформ.

На момент старта проекта у компании уже имелось функционирующее продуктовое решение. Однако реализация решения на MSSQL не могла в полной мере соответствовать ожиданиям по масштабированию функционала с сохранением приемлемой стоимости доработки.

Стоявшая перед нами задача была поистине амбициозной Неофлекс и Mediascope предстояло создать промышленное решение менее чем за год, при условии выхода MVP уже в течение первого квартала от даты начала работ.

В качестве фундамента для построения новой платформы данных, основанной на low-code-вычислениях, был выбран стек технологий Hadoop. Стандартом хранения данных стал HDFS с использованием файлов формата parquet. Для доступа к данным, находящимся в платформе, использован Hive, в котором все доступные витрины представлены в виде внешних таблиц. Загрузка данных в хранилище реализовывалась с помощь Kafka и Apache NiFi.

Lowe-code-инструмент в данной концепции был применён для оптимизации самой трудозатратной задачи в построении аналитической платформы задачи расчёта данных.



Основным механизмом для маппирования данных был выбран low-code-инструмент Datagram. Neoflex Datagram это средство для разработки трансформаций и потоков данных.
Применяя данный инструмент, можно обойтись без написания кода на Scala вручную. Scala-код генерируется автоматически с использованием подхода Model Driven Architecture.

Очевидный плюс такого подхода ускорение процесса разработки. Однако помимо скорости есть ещё и следующие достоинства:

  • Просмотр содержимого и структуры источников/приемников;
  • Отслеживание происхождения объектов потока данных до отдельных полей (lineage);
  • Частичное выполнение преобразований с просмотром промежуточных результатов;
  • Просмотр исходного кода и его корректировка перед выполнением;
  • Автоматическая валидация трансформаций;
  • Автоматическая загрузка данных 1 в 1.

Порог вхождения в low-code-решения для генерации трансформаций достаточно невысок: разработчику необходимо знать SQL и иметь опыт работы с ETL-инструментами. При этом стоит оговориться, что code-driven-генераторы трансформаций это не ETL-инструменты в широком понимании этого слова. Low-code-инструменты могут не иметь собственного окружения для выполнения кода. То есть сгенерированный код будет выполняться на том окружении, которое имелось на кластере ещё до инсталляции low-code-решения. И это, пожалуй, ещё один плюс в карму low-code. Так как в параллель с low-code-командой может работать классическая команда, реализующая функционал, например, на чистом Scala-коде. Втягивание доработок обеих команд в продуктив будет простым и бесшовным.

Пожалуй, стоит ещё отметить, что помимо low-code есть ещё и no-code решения. И по своей сути это разные вещи. Low-code в большей степени позволяет разработчику вмешиваться в генерируемый код. В случае с Datagram возможен просмотр и редактирование генерируемого кода Scala, no-code такой возможности может не предоставлять. Эта разница весьма существенна не только в плане гибкости решения, но и в плане комфорта и мотивации в работе дата-инженеров.

Архитектура решения


Давайте попробуем разобраться, как именно low-code-инструмент помогает решить задачу оптимизации скорости разработки функционала расчёта данных. Для начала разберём функциональную архитектуру системы. В данном случае примером выступает модель производства данных для медиаисследований.



Источники данных в нашем случае весьма разнородны и многообразны:

  • Пиплметры (ТВ-метры) программно-аппаратные устройства, считывающие пользовательское поведение у респондентов телевизионной панели кто, когда и какой телеканал смотрел в домохозяйстве, которое участвует в исследовании. Поставляемая информация это поток интервалов смотрения эфира с привязкой к медиапакету и медиапродукту. Данные на этапе загрузки в Data Lake могут быть обогащены демографическими атрибутами, привязкой к геострате, таймзоне и другими сведениями, необходимыми для проведения анализа телепросмотра того или иного медиа продукта. Произведённые измерения могут быть использованы для анализа или планирования рекламных компаний, оценки активности и предпочтений аудитории, составления эфирной сетки;
  • Данные могут поступать из систем мониторинга потокового телевещания и замера просмотра контента видеоресурсов в интернете;
  • Измерительные инструменты в web-среде, среди которых как site-centric, так и user-centric счётчики. Поставщиком данных для Data Lake может служить надстройка браузера research bar и мобильное приложение со встроенным VPN.
  • Данные также могут поступать с площадок, консолидирующих результаты заполнения онлайн-анкет и итоги проведения телефонных интервью в опросных исследованиях компании;
  • Дополнительное обогащение озера данных может происходить за счёт загрузки сведений из логов компаний-партнёров.

Имплементация as is загрузки из систем-источников в первичный staging сырых данных может быть организована различными способами. В случае использования для этих целей low-code возможна автоматическая генерация сценариев загрузки на основе метаданных. При этом нет необходимости спускаться на уровень разработки source to target мэппингов. Для реализации автоматической загрузки нам необходимо установить соединение с источником, после чего определить в интерфейсе загрузки перечень сущностей, подлежащих загрузке. Создание структуры каталогов в HDFS произойдёт автоматически и будет соответствовать структуре хранения данных в системе-источнике.

Однако в контексте данного проекта эту возможность low-code-платформы мы решили не использовать в силу того, что компания Mediascope уже самостоятельно начала работу по изготовлению аналогичного сервиса на связке Nifi + Kafka.

Стоит сразу обозначить, что данные инструменты являются не взаимозаменяющими, а скорее дополняющими друг друга. Nifi и Kafka способны работать как в прямой (Nifi -> Kafka), так и в обратной (Kafka -> Nifi) связке. Для платформы медиаисследований использовался первый вариант связки.



В нашем случае найфаю требовалось обрабатывать различные типы данных из систем-источников и пересылать их брокеру Kafka. При этом направление сообщений в определённый топик Kafka производилось посредством применения Nifi-процессоров PublishKafka. Оркестрация и обслуживание этих pipeline`ов производится в визуальном интерфейсе. Инструмент Nifi и использование связки Nifi + Kafka также можно назвать low-code-подходом к разработке, обладающим низким порогом вхождения в технологии Big Data и ускоряющим процесс разработки приложений.

Следующим этапом в реализации проекта являлось приведение к формату единого семантического слоя детальных данных. В случае наличия у сущности исторических атрибутов расчёт производится в контексте рассматриваемой партиции. Если же сущность не является исторической, то опционально возможен либо пересчёт всего содержимого объекта, либо вовсе отказ от пересчёта этого объекта (вследствие отсутствия изменений). На данном этапе происходит генерация ключей для всех сущностей. Ключи сохраняются в соответствующие мастер-объектам справочники Hbase, содержащие соответствие между ключами в аналитической платформе и ключами из систем-источников. Консолидация атомарных сущностей сопровождается обогащением результатами предварительного расчёта аналитических данных. Framework`ом для расчёта данных являлся Spark. Описанный функционал приведения данных к единой семантике был реализован также на основе маппингов low-code-инструмента Datagram.

В целевой архитектуре требовалось обеспечить наличие SQL-доступа к данным для бизнес-пользователей. Для данной опции был использован Hive. Регистрация объектов в Hive производится автоматически при включении опции Registr Hive Table в low-code-инструменте.



Управление потоком расчёта


Datagram имеет интерфейс для построения дизайна потоков workflow. Запуск маппингов может осуществляться с использованием планировщика Oozie. В интерфейсе разработчика потоков возможно создание схем параллельного, последовательного или зависящего от заданных условий исполнения преобразований данных. Имеется поддержка shell scripts и java-программ. Также возможно использование сервера Apache Livy. Apache Livy используется для запуска приложений непосредственно из среды разработки.

В случае, если у компании уже есть собственный оркестратор процессов, возможно использование REST API для встраивания маппингов в уже имеющийся поток. Например, у нас имелся достаточно успешный опыт встраивания маппингов на Scala в оркестраторы, написанные на PLSQL и Kotlin. REST API малокодового инструмента подразумевает наличие таких операций, как генерация исполняемого года на основе дизайна маппинга, вызов маппинга, вызов последовательности маппингов и, разумеется, передача в URL параметров для запуска маппингов.

Наравне с Oozie возможно организовать поток расчёта средствами Airflow. Пожалуй, не буду долго останавливаться на сравнении Oozie и Airflow, а просто скажу, что в контексте работ по проекту медиаисследований выбор пал в сторону Airflow. Главными аргументами на этот раз оказались более активное сообщество, развивающее продукт, и более развитый интерфейс + API.



Airflow также хорош потому, что для описания процессов расчёта в нём используется многими любимый Python. Да и вообще, платформ управления рабочими процессами с открытым исходным кодом не так уж и много. Запуск и мониторинг выполнения процессов (в том числе с диаграммой Ганта) лишь добавляют очков в карму Airflow.

Форматом конфигурационного файла для запуска маппингов low-code-решения стал spark-submit. Произошло это по двум причинам. Во-первых, spark-submit позволяет напрямую запустить jar-файл из консоли. Во-вторых, он может содержать всю необходимую информацию для конфигурирования рабочего потока (что облегчает написание скриптов, формирующих Dag).
Наиболее часто встречающимся элементом рабочего потока Airflow в нашем случае стал SparkSubmitOperator.

SparkSubmitOperator позволяет запускать jar`ники упакованные маппинги Datagram с предварительно сформированными для них входными параметрами.

Следует упомянуть, что каждая задача Airflow выполняется в отдельном потоке и ничего не знает о других задачах. В связи с чем взаимодействие между задачами осуществляется с помощью управляющих операторов, таких как DummyOperator или BranchPythonOperator.

В совокупности использования low-code-решения Datagram в связке с универсализацией конфигурационных файлов (формирующих Dag) привело к существенному ускорению и упрощению процесса разработки потоков загрузки данных.

Расчёт витрин


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



Отдельным шагом подготовки витрин является валидация данных. Алгоритм валидации сопряжён с применением ряда математических science-моделей. Однако использование low-code-платформы позволяет разбить сложный алгоритм на ряд отдельных визуально считываемых маппингов. Каждый из маппингов выполняет узкую задачу. Вследствие чего возможен промежуточный дебаг, логирование и визуализация этапов подготовки данных.

Алгоритм валидации было решено дискретизировать на следующие подэтапы:

  • Построение регрессий зависимостей смотрения телесети в регионе со смотрением всех сетей в регионе за 60 дней.
  • Расчёт стьюдентизированных остатков (отклонения фактических значений от предсказанных регрессионной моделью) для всех точек регрессии и для расчетного дня.
  • Выборка аномальных пар регион-телесеть, где стьюдентизированный остаток расчетного дня превышает норму (заданную настройкой операции).
  • Пересчёт поправленного стьюдентизированного остатка по аномальным парам регион-телесеть для каждого респондента, смотревшего сеть в регионе с определением вклада данного респондента (величина изменения стьюдентизированного остатка) при исключении смотрения данного респондента из выборки.
  • Поиск кандидатов, исключение которых приводит стьюдентизированный остаток расчетного дня в норму;

Приведённый выше пример является подтверждением гипотезы о том, что у дата-инженера и так слишком много чего должно быть в голове И, если это действительно инженер, а не кодер, то страх профессиональной деградации при использовании low-code-инструментов у него должен окончательно отступить.

Что ещё может low-code?


Область применения low-code инструмента для пакетной и потоковой обработки данных без необходимости написания кода на Scala вручную не заканчивается.

Применение low-code в разработке datalake`ов для нас стало уже некоторым стандартом. Наверное, можно сказать, что решения на стеке Hadoop повторяют путь развития классических DWH, основанных на РСУБД. Малокодовые инструменты на стеке Hadoop могут решать, как задачи обработки данных, так и задачи построения конечных BI-интерфейсов. Причём нужно заметить, что под BI может пониматься не только репрезентация данных, но и их редактирование силами бизнес-пользователей. Данный функционал нами часто применяется при построении аналитических платформ для финансового сектора.



Среди прочего, с помощью low-code и, в частности, Datagram возможно решить задачу отслеживания происхождения объектов потока данных с атомарностью до отдельных полей (lineage). Для этого в low-code-инструменте имплементировано сопряжение с Apache Atlas и Cloudera Navigator. По сути, разработчику необходимо зарегистрировать набор объектов в словарях Atlas и ссылаться на зарегистрированные объекты при построении маппингов. Механизм отслеживания происхождения данных или анализ зависимостей объектов экономит большое количество времени при необходимости внесения доработок в алгоритмы расчёта. Например, при построении финансовой отчётности эта фишка позволяет комфортнее пережить период изменений законодательства. Ведь, чем качественнее мы осознаём межформенную зависимость в разрезе объектов детального слоя, тем меньше мы столкнёмся с внезапными дефектами и сократим количество реворков.



Data Quality & Low-code


Ещё одной задачей, реализованной low-code-инструментом на проекте компании Mediascope, стала задача класса Data Quality. Особенностью реализации конвейера проверки данных для проекта исследовательской компании было отсутствие влияния на работоспособность и скорость работы основного потока расчёта данных. Для возможности оркестрирования независимыми потоками проверки данных применялся уже знакомый Apache Airflow. По мере готовности каждого шага производства данных параллельно происходил запуск обособленной части DQ-конвейера.

Хорошей практикой считается наблюдение за качеством данных с момента их зарождения в аналитической платформе. Имея информацию о метаданных, мы можем уже с момента попадания информации в первичный слой проверять соблюдение базовых условий not null, constraints, foreign keys. Этот функционал реализован на основе автоматически генерируемых мэппингов семейства data quality в Datagram. Кодогенерация в данном случае также основывается на метаданных модели. На проекте компании Mediascope сопряжение происходило с метаданными продукта Enterprise Architect.

Благодаря сопряжению low-code-инструмента и Enterprise Architect автоматически были сгенерированы следующие проверки:

  • Проверка присутствия значений null в полях с модификатором not null;
  • Проверка присутствия дублей первичного ключа;
  • Проверка внешнего ключа сущности;
  • Проверка уникальности строки по набору полей.

Для более сложных проверок доступности и достоверности данных был создан мэппинг с Scala Expression, принимающий на вход внешний Spark SQL-код проверки, подготовленной силами аналитиков в Zeppelin.



Разумеется, к автогенерации проверок необходимо приходить постепенно. В рамках описываемого проекта этому предшествовали следующие шаги:
  • DQ, реализованные в блокнотах Zeppelin;
  • DQ, встроенные в мэппинг;
  • DQ в виде отдельных массивных мэппингов, содержащих целый набор проверок под отдельную сущность;
  • Универсальные параметризованные DQ-мэппинги, принимающие на вход информацию о метаданных и бизнес-проверках.

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

  • Все проверки метаданных генерируются автоматически при изменении модели в EA;
  • Проверки доступности данных (определение наличия каких-либо данных на момент времени) могут быть сгенерированы на основе справочника, хранящего ожидаемый тайминг появления очередной порции данных в разрезе объектов;
  • Бизнес-проверки достоверности данных создаются силами аналитиков в notebook`ах Zeppelin. Откуда направляются прямиком в настроечные таблицы модуля DQ на продукционном окружении.

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

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

Вместо заключения


Преимущество применения low-code очевидно. Разработчикам не нужно разрабатывать приложение с нуля. А освобождённый от дополнительных задач программист даёт результат быстрее. Скорость, в свою очередь, высвобождает дополнительный ресурс времени на разрешение вопросов оптимизации. Следовательно, в данном случае можно рассчитывать на наличие более качественного и быстрого решения.

Разумеется, low-code не панацея, и волшебство само по себе не случится:

  • Малокодовая индустрия проходит стадию крепчания, и пока в ней нет однородных индустриальных стандартов;
  • Многие low-code-решения не бесплатны, и их приобретение должно быть осознанным шагом, сделать который следует при полной уверенности финансовой выгоды от их использования;
  • Многие малокодовые решения не всегда хорошо дружат с GIT / SVN. Либо неудобны в использовании в случае сокрытия генерируемого кода;
  • При расширении архитектуры может потребоваться доработка малокодового решения что, в свою очередь, провоцирует эффект привязанности и зависимости от поставщика low-code-решения.
  • Должный уровень обеспечения безопасности возможен, но весьма трудозатратен и сложен в реализации движков low-code-систем. Малокодовые платформы должны выбираться не только по принципу поиска выгоды от их использования. При выборе стоит задаться вопросами наличия функционала управлением доступа и делегированием/эскалацией идентификационных данных на уровень всего IT-ландшафта организации.



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

Если один разработчик на low-code-платформе будет выполнять свою работу быстрее, чем два разработчика без low-code, то это даёт компании фору во всех отношениях. Порог вхождения в low-code-решения более низкий, чем в традиционные технологии, и это положительным образом сказывается на вопросе кадрового дефицита. При использовании малокодовых инструментов возможно ускорение взаимодействия между функциональными командами и более быстрое принятие решений о корректности выбранного пути data-science-исследований. Низкоуровневые платформы могут выступить причиной цифровой трансформации организации, поскольку производимые решения могут быть доступны к пониманию нетехническим специалистам (в частности, бизнес-пользователям).

Если у вас сжатые сроки, нагруженная бизнес-логика, дефицит технологической экспертизы, и вам требуется ускорить time to market, то low-code это один из способов удовлетворения ваших потребностей.

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

Мультитул для управления Хранилищем Данных кейс Wheely dbt

30.03.2021 00:12:15 | Автор: admin

Уже более двух лет data build tool активно используется в компании Wheely для управления Хранилищем Данных. За это время накоплен немалый опыт, мы на тернистом пути проб и ошибок к совершенству в Analytics Engineering.

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

Поэтому сегодня я предлагаю вам экскурсию по Хранилищу Данных Wheely. В формат публикации я попытался уложить самые яркие моменты и впечатления от использования dbt, снабдив реальными примерами, практиками и опытом. Добро пожаловать под кат.

Структура превыше всего

Измерять сложность Хранилища Данных в количестве гигабайт сегодня - дурной тон

Налить кучу тяжело интерпретируемых данных без метаинформации (читай мусора) не составит большого труда. Гораздо сложнее из этих данных получить что-то осмысленное. То, на что с уверенностью могут опираться business stakeholders, принимая решения. То, что регулярно измеряется на предмет качества и актуальности. Наконец, то, что соответствует принципам Keep it simple (KISS) и Dont repeat yourself (DRY).

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

Схема слоев Хранилища ДанныхСхема слоев Хранилища Данных

Зеленым цветом слой источников данных sources. Это реплики структур и таблиц из исходных систем, которые поддерживаются ELT-сервисом. Данные синхронизируются 1:1 с источником, без каких-либо преобразований. Опциональный слой flatten позволяет вложенные иерархические структуры (JSON) превратить в плоские таблицы.

Слой staging предназначен для простых преобразований: переименование полей, преобразование типов, расчет новых колонок с помощью конструкции case. На этом этапе мы готовим почву для дальнейших преобразований, приводим всё к единому виду и неймингу.

Intermediate или промежуточный слой отвечает за формирование предварительных таблиц и агрегатов, где происходит обогащение данных. Для ряда бизнес-областей мы не используем этот слой, для других логика может насчитывать до 5-10 взаимосвязанных моделей.

Кульминацией являются data marts или Витрины Данных, которые используются Data Scientists / Business Users / BI tools. Слой, в свою очередь, делится на:

  • dimensions: пользователи, компании, машины, водители, календарь

  • facts: поездки, транзакции, сеансы, продвижения, коммуникации

  • looker: материализованные представления и витрины, оптимизированные под чтение из BI-системы

Число 120 из заголовка публикации относится только к витринам данных:

Running with dbt=0.19.0Found 273 models, 493 tests, 6 snapshots, 4 analyses, 532 macros, 7 operations, 8 seed files, 81 sources, 0 exposures

На текущий момент в проекте:

  • 273 модели во всех перечисленных слоях

  • 493 теста на эти модели, включая not null, unique, foreign key, accepted values

  • 6 снапшотов для ведения истории SCD (slowly changing dimensions)

  • 532 макроса (большая часть из которых импортирована из сторонних модулей)

  • 7 operations включая vacuum + analyze

  • 81 источник данных

Помимо разбиения на логические слои, Хранилище можно нарезать по бизнес-областям. В случае необходимости есть возможность пересчитать или протестировать витрины, относящиеся к вертикалям Marketing / Supply / Growth / B2B. Например, в случае late arriving data или ручных корректировках маппингов/справочников.

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

dbt run -m +tag:marketing

Этот же принцип лежит в основе организации кодой базы. Все скрипты объединены в директории с общей логикой и понятными наименованиями. Сложно потеряться даже при огромном количестве моделей и витрин:

Иерархия проекта dbt
.|____staging| |____webhook| |____receipt_prod| |____core| |____wheely_prod| |____flights_prod| |____online_hours_prod| |____external| |____financial_service|____marts| |____looker| |____dim| |____snapshots| |____facts|____flatten| |____webhook| |____receipt_prod| |____wheely_prod| |____communication_prod|____audit|____sources|____aux| |____dq| | |____marts| | |____external|____intermediate

Оптимизация физической модели

Логическое разделение на слои и области - это замечательно. Но не менее важно и то, как эта логика ложится на конкретную СУБД. В случае Wheely это Amazon Redshift.

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

Цепочка зависимостей витрины поездок (journeys)Цепочка зависимостей витрины поездок (journeys)

На этапе обогащения данных важна скорость склейки таблиц (join performance), поэтому данные сегментированы и отсортированы в одинаковом ключе, начиная с sources. Это позволит использовать самый быстрый вид соединения - sort merge join:

Конфигурация для оптимального соединения sort merge join
{{config(materialized='table',unique_key='request_id',dist="request_id",sort="request_id")}}

Витрина же хранится отсортированной по самым популярным колонкам доступа: city, country, completed timestamp, service group. В случае правильного подбора колонок Interleaved key позволяет значительно оптимизировать I/O и ускорить отрисовку графиков в BI-системах.

Конфигурация для быстрого чтения витрины interleaved sortkey
{{config(materialized='table',unique_key='request_id',dist="request_id",sort_type='interleaved',sort=["completed_ts_loc", "city", "country", "service_group", "is_airport", "is_wheely_journey"])}}

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

staging:+materialized: view+schema: staging+tags: ["staging"]

Другой интересный пример результаты проверки качества данных. Выбранный тип материализации ephemeral, т.е. на уровне СУБД не будет создано ни таблицы, ни представления. При каждом обращении к такой модели будет выполнен лишь запрос. Результат такого запроса является слагаемым в суммарной таблице, содержащей метрики всех проверяемых объектов.

В свою очередь большие таблицы фактов имеет смысл наполнять инкрементально. Особенно при условии того, что факт, случившийся однажды, больше не меняет своих характеристик. Таким образом мы процессим только изменения (delta) новые факты, произошедшие после последнего обновления витрины. Обратите внимание на условие where:

Пример инкрементального наполнения витрины
{{config(materialized='incremental',sort='metadata_timestamp',dist='fine_id',unique_key='id')}}with fines as (selectfine_id, city_id, amount, details, metadata_timestamp, created_ts_utc, updated_ts_utc, created_dt_utcfrom {{ ref('stg_fines') }}where true-- filter fines arrived since last processed time{% if is_incremental() -%}and metadata_timestamp > (select max(metadata_timestamp) from {{ this }}){%- endif %}),...

Кстати, о принципах MPP и о том, как выжать максимум из аналитических СУБД я рассказываю на курсах Data Engineer и Data Warehouse Analyst (скоро первый запуск!).

SQL + Jinja = Flexibility

Высокоуровневый декларативный язык SQL прекрасен сам по себе, но вкупе с движком шаблонизации Jinja он способен творить чудеса.

Любой код, который вы используете с dbt проходит этапы compile & run. На этапе компиляции интерпретируются все шаблонизированные выражения и переменные. На этапе запуска код оборачивается в конструкцию CREATE в зависимости от выбранного типа материализации и фишек используемой СУБД: clustered by / distributed by / sorted by. Рассмотрим пример:

Model code:
{{config(materialized='table',dist="fine_id",sort="created_ts_utc")}}with details as (  select{{dbt_utils.star(from=ref('fine_details_flatten'),except=["fine_amount", "metadata_timestamp", "generated_number"])}}from {{ ref('fine_details_flatten') }}where fine_amount > 0)select * from details
Compiled code:
with details as (select  "id","fine_id","city_id","amount","description","created_ts_utc","updated_ts_utc","created_dt_utc"from "wheely"."dbt_test_akozyr"."fine_details_flatten"where fine_amount > 0)select * from details
Run code:
create table"wheely"."dbt_test_akozyr"."f_chauffeurs_fines"diststyle key distkey (fine_id)compound sortkey(created_ts_utc)as (with details as (select"id","fine_id","city_id","amount","description","created_ts_utc","updated_ts_utc","created_dt_utc"from "wheely"."dbt_test_akozyr"."fine_details_flatten"where fine_amount > 0)select * from details);

Ключевым моментом является тот факт, что пишете вы только лаконичный шаблонизированный код, а остальным занимается движок dbt. Написание boilerplate code сведено к минимуму. Фокус инженера или аналитика остается преимущественно на реализуемой логике.

Во-вторых, как происходит выстраивание цепочки связей и очередности создания витрин, продемонстрированные на картинках выше? Внимательный читатель уже заметил, что в рамках написания кода при ссылках на другие модели нет хардкода, но есть конструкция {{ ref('fine_details_flatten') }} ссылка на наименование другой модели. Она и позволяет распарсить весь проект и построить граф связей и зависимостей. Так что это тоже делается абсолютно прозрачным и органичным способом.

С помощью шаблонизации Jinja в проекте Wheely мы гибко управляем схемами данных и разделением сред dev / test / prod. В зависимости от метаданных подключения к СУБД будет выбрана схема и период исторических данных. Продакшн модели создаются в целевых схемах под технической учетной записью. Аналитики же ведут разработку каждый в своей личной песочнице, ограниченной объемом данных в 3-е последних суток. Это реализуется с помощью макроса:

Макрос управления схемами для подключений:
{% macro generate_schema_name_for_env(custom_schema_name, node) -%}{%- set default_schema = target.schema -%}{%- if target.name == 'prod' and custom_schema_name is not none -%}{{ custom_schema_name | trim }}{%- else -%}{{ default_schema }}{%- endif -%}{%- endmacro %}

Еще одним важным преимуществом является самодокументируемый код. Иными словами, из репозитория проекта автоматически можно собрать статический сайт с документацией: перечень слоев, моделей, атрибутный состав, метаинформацию о таблицах в СУБД и даже визуализировать граф зависимостей (да-да, картинки выше именно оттуда).

Не повторяйся лучше подготовь макрос

Однотипный код, повторяющиеся обращения и действия, зачастую реализуемые по принципу copy-paste нередко являются причиной ошибок и багов. В Wheely мы придерживаемся принципа Do not repeat yourself и любой сколько-нибудь похожий код шаблонизируем в макрос с параметрами. Писать и поддерживать такой код становится сплошным удовольствием.

Простой пример с конвертацией валют:
-- currency conversion macro{% macro convert_currency(convert_column, currency_code_column) -%}( {{ convert_column }} * aed )::decimal(18,4) as {{ convert_column }}_aed, ( {{ convert_column }} * eur )::decimal(18,4) as {{ convert_column }}_eur, ( {{ convert_column }} * gbp )::decimal(18,4) as {{ convert_column }}_gbp, ( {{ convert_column }} * rub )::decimal(18,4) as {{ convert_column }}_rub, ( {{ convert_column }} * usd )::decimal(18,4) as {{ convert_column }}_usd{%- endmacro %}
Вызов макроса из модели:
select...-- price_details, r.currency, {{ convert_currency('price', 'currency') }}, {{ convert_currency('transfer_min_price', 'currency') }}, {{ convert_currency('discount', 'currency') }}, {{ convert_currency('insurance', 'currency') }}, {{ convert_currency('tips', 'currency') }}, {{ convert_currency('parking', 'currency') }}, {{ convert_currency('toll_road', 'currency') }}, {{ convert_currency('pickup_charge', 'currency') }}, {{ convert_currency('cancel_fee', 'currency') }}, {{ convert_currency('net_bookings', 'currency') }}, {{ convert_currency('gross_revenue', 'currency') }}, {{ convert_currency('service_charge', 'currency') }}...from {{ ref('requests_joined') }} r

По большому счету, макрос это просто вызов функции с передачей аргументов, на уже знакомом вам диалекте Jinja. Результатом работы макроса является готовый к исполнению SQL-скрипт. Макрос для кросс-сверки значений в колонках:

Сравнить значения двух колонок
-- compare two columns{% macro dq_compare_columns(src_column, trg_column, is_numeric=false) -%}{%- if is_numeric == true -%}{%- set src_column = 'round(' + src_column + ', 2)' -%}{%- set trg_column = 'round(' + trg_column + ', 2)' -%}{%- endif -%}CASEWHEN {{ src_column }} = {{ trg_column }} THEN 'match'WHEN {{ src_column }} IS NULL AND {{ trg_column }} IS NULL THEN 'both null'WHEN {{ src_column }} IS NULL THEN 'missing in source'WHEN {{ trg_column }} IS NULL THEN 'missing in target'WHEN {{ src_column }} <> {{ trg_column }} THEN 'mismatch'ELSE 'unknown'END{%- endmacro %}

В макрос можно запросто записать даже создание UDF-функций:

Создать UDF
-- cast epoch as human-readable timestamp{% macro create_udf() -%}{% set sql %}CREATE OR REPLACE FUNCTION {{ target.schema }}.f_bitwise_to_delimited(bitwise_column BIGINT, bits_in_column INT)RETURNS VARCHAR(512)STABLEAS $$# Convert column to binary, strip "0b" prefix, pad out with zeroesif bitwise_column is not None:b = bin(bitwise_column)[2:].zfill(bits_in_column)[:bits_in_column+1]return belse:None$$ LANGUAGE plpythonu;CREATE OR REPLACE FUNCTION {{ target.schema }}.f_decode_access_flags(access_flags INT, deleted_at TIMESTAMP)RETURNS VARCHAR(128)STABLEAS $$SELECT nvl(DECODE($2, null, null, 'deleted'), DECODE(LEN(analytics.f_bitwise_to_delimited($1, 7))::INT, 7, null, 'unknown'), DECODE(analytics.f_bitwise_to_delimited($1, 7)::INT, 0, 'active', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 1, 1), 1, 'end_of_life', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 7, 1), 1, 'pending', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 6, 1), 1, 'rejected', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 5, 1), 1, 'blocked', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 4, 1), 1, 'expired_docs', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 3, 1), 1, 'partner_blocked', null), DECODE(SUBSTRING(analytics.f_bitwise_to_delimited($1, 7), 2, 1), 1, 'new_partner', null))$$ LANGUAGE SQL;{% endset %}{% set table = run_query(sql) %}{%- endmacro %}

Параметризовать можно и довольно сложные вещи, такие как работа с nested structures (иерархическими структурами) и выгрузка во внешние таблицы (external tables) в S3 в формате parquet. Эти примеры вполне достойны отдельных публикаций.

Не изобретай велосипед импортируй модули

Модуль или package - это набор макросов, моделей, тестов, который можно импортировать в свой проект в виде готовой к использованию библиотеки. На портале dbt hub есть неплохая подборка модулей на любой вкус, и, что самое главное, их список постоянно пополняется.

С помощью модуля логирования и добавления 2 простых hooks на каждый запуск dbt у меня как на ладони появляется статистическая информация о времени, продолжительности, флагах и параметрах развертывания. Я наглядно вижу модели анти-лидеры по потребляемым ресурсам (первые кандидаты на рефакторинг):

models:+pre-hook: "{{ logging.log_model_start_event() }}"+post-hook: "{{ logging.log_model_end_event() }}"
Мониторинг развертывания dbt моделей на кластере RedshiftМониторинг развертывания dbt моделей на кластере Redshift

Измерение календаря собирается в одну строку, при этом набор колонок поражает:

{{ dbt_date.get_date_dimension('2012-01-01', '2025-12-31') }}
Измерение календарь, сгенерированное макросомИзмерение календарь, сгенерированное макросом

С помощью модуля dbt_external_tables я уже выстраиваю полноценный Lakehouse, обращаясь из Хранилища к данным, расположенным в файловом хранилище S3. К примеру, самые свежие курсы валют, получаемые через API Open Exchange Rates в формате JSON:

External data stored in S3 accessed vith Redshift Spectrum
- name: externalschema: spectrumtags: ["spectrum"]description: "External data stored in S3 accessed vith Redshift Spectrum"tables:- name: currencies_oxrdescription: "Currency Exchange Rates fetched from OXR API https://openexchangerates.org"freshness:error_after: {count: 15, period: hour}loaded_at_field: timestamp 'epoch' + "timestamp" * interval '1 second'external:location: "s3://data-analytics.wheely.com/dwh/currencies/"row_format: "serde 'org.openx.data.jsonserde.JsonSerDe'"columns:- name: timestampdata_type: bigint- name: basedata_type: varchar(3)- name: ratesdata_type: struct<aed:float8, eur:float8, gbp:float8, rub:float8, usd:float8>

Ну и, конечно, ночью по расписанию работает VACUUM + ANALYZE, ведь Redshift это форк PostgreSQL. Дефрагментация, сортировка данных в таблицах, сбор статистик. Иначе говоря поддержание кластера в тонусе, пока dba спит.

dbt run-operation redshift_maintenance --args '{include_schemas: ["staging", "flatten", "intermediate", "analytics", "meta", "snapshots", "ad_hoc"]}'
VACUUM + ANALYZEVACUUM + ANALYZE

Running in production: используем dbt Cloud в Wheely

dbt Cloud это платный сервис для управления проектами, основанными на движке dbt. За небольшие деньги команда получает возможность создавать окружения, конфигурировать джобы и таски, устанавливать расписание запусков, и даже полноценную IDE (среду разработки!) в браузере.

Прежде всего речь идет об удобстве использования: приятный и понятный визуальный интерфейс, простота поиска и ориентирования, акцентирование ключевой информации при разборе ошибок и чтении логов:

Во-вторых, это гибкие настройки условий запуска джобов. Начиная от простых условий с выбором дня недели и времени, продолжая кастомными cron-выражениями, и заканчивая триггером запуска через webhook. Например, именно через вебхук мы связываем в цепочку завершение выгрузок для кросс-сверки и начало расчета соответствующих витрин в Хранилище (kicked off from Airflow):

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

Сам dbt является проектом с открытым исходным кодом, и использование продукта dbt Cloud представляется очень удобным, но не обязательным. В качестве альтернативных способов можно выбрать любой другой оркестратор: Airflow, Prefect, Dagster, и даже просто cron. В своем проекте Сквозная Аналитика я организую оркестрацию при помощи Github Actions. Выходит очень занятно.

Вместо заключения

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

Сегодня бизнес и команда активно растут. Доступен ряд интересных позиций:

У тебя есть возможность узнать детали из первых уст и получить прямую рекомендацию.

Также время от времени я провожу вебинары и выступления, на которых подробнее рассказываю о своей работе и проектах. Следить за моими публикациями можно в телеграм-канале Technology Enthusiast https://t.me/enthusiastech

Пишите, задавайте вопросы и, конечно, пробуйте dbt в своих проектах!

Подробнее..

Как мы выбирали Data Catalog, но в итоге оставили все как есть

09.04.2021 12:18:12 | Автор: admin

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


Периодически нам приходится отвечать на вопросы, где у нас лежат те или иные данные. Поэтому однажды мы решили провести эксперимент и внедрить Data Catalog, чтобы запросы приходили уже не к нам, а в систему. Например, если человеку понадобилась информация, связанная с заказами, он может перейти в систему, ввести слово order и найти все, что ему нужно по этой теме. Мы рассмотрели три инструмента и в итоге не стали ничего менять. Рассказываю почему.



В идеальном мире Data Catalog это инструмент, в котором можно найти краткую сводку по данным в хранилище, увидеть их структуру, проследить lineage (путь данных от системы-источника до целевой таблицы), посмотреть profiling (краткую статистику по полям таблицы) и историю проверок качества данных, увидеть владельцев данных и запросить доступ. Сейчас у нас есть подобие этого каталога: все таблицы нашего хранилища описываются вручную аналитиками в Confluence.


Мы решили поставить небольшой эксперимент и представить, что было бы, если роль Data Catalog исполнял не Confluence, а другая система.


Требования к системе


Мы определили несколько важных требований к потенциальной системе, в которой бы начали строить Data Catalog:


  • Автоматический сбор данных из разных СУБД. Это позволит нам избавить аналитиков от ручного обновления описаний таблиц.
  • Отображение структуры датасета с понятными описаниями и полнотекстовым поиском по этой информации.
  • Web UI с поиском. Это очень важное требование, поскольку в первую очередь Data Catalog задумывается как инструмент для поиска метаданных.
  • Визуализация data lineage от системы-источника до отчета в BI-системе.
  • Отображение data owner. С помощью этого можно понять, к какому человеку обратиться по всем вопросам, связанных с данными.

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


  • SSO SAML авторизация;
  • визуализация Data Profiling;
  • визуализация Data Quality;
  • добавление кастомной информации для отображения;
  • трекинг изменения датасетов.

Мы решили рассмотреть три популярных open source проекта: Amundsen, LinkedIn DataHub и Marquez.


Amundsen



Amundsen это типичный справочник. То есть просто хорошая штука, чтобы поискать информацию по имеющимся таблицам. Он состоит из следующих сервисов:


  • neo4j хранилище метаданных (также может использоваться Apache Atlas);
  • elasticsearch поисковый движок;
  • amundsensearch сервис для поиска по данным в Elasticsearch;
  • amundsenfrontendlibrary Web UI (написан на Flask);
  • amundsenmetadatalibrary отвечает за работу с метаданными в Neo4j или Atlas;
  • amundsendatabuilder библиотека для извлечения данных из различных СУБД.


Принцип работы довольно простой. ETL-процесс сбора метаданных состоит из извлечения записей из источника при помощи выполнения SQL-запросов, преобразования записей и их загрузки в хранилище метаданных. Extractor выполняет запрос к хранилищу метаданных и преобразует их в набор вершин и связей между ними. Промежуточные результаты сохраняются в локальную директорию. Transformer преобразует загруженные данные в нужную структуру. Loader подхватывает промежуточные данные и складывает их либо во временный слой, либо сразу в финальное хранилище. Publisher подхватывает промежуточные данные и отправляет в хранилище.


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


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


Плюсы:


  • автоматический сбор данных из разных СУБД;
  • API для добавления или редактирования данных в автоматическом режиме за счет обращения напрямую к Metastore/information_schema;
  • Web UI с полнотекстовым поиском;
  • поиск по базам, таблицам, полям и тэгам;
  • добавление кастомной информации для отображения (programmatic description)
  • визуализация data profiling (например, количество записей, дата последнего обновления, исторические значения);
  • визуализация data quality (какие проверки навешаны на датасет, история результатов проверок);
  • отображение data owner.

Минусы:


  • нет трекинга изменения датасетов (хранит только актуальное состояние и работает как справочник);
  • нет data lineage (источник можно идентифицировать только в блоке с кастомной информацией);
  • не нашли SSO-аутентификацию, доступна только OIDC;
  • полнотекстовой поиск работает только для тегов, таблиц, баз и колонок (нет возможности искать по описаниям колонок).

LinkedIn DataHub



Как можно понять из названия, это платформа поиска и обнаружения метаданных от LinkedIn. Из коробки она состоит из целого зоопарка сервисов:


  • kafka-broker брокер Kafka;
  • zookeeper координатор для Kafka;
  • kafka-rest-proxy RESTful интерфейс для Kafka;
  • kafka-topics-ui Web UI для топиков Kafka;
  • schema-registry Kafka Schema Registry;
  • schema-registry-ui Kafka Schema Registry UI;
  • elasticsearch поисковый движок;
  • kibana дашборд для Elasticsearch;
  • neo4j графовая база данных;
  • datahub-gms Generalized Metadata Store;
  • datahub-frontend Web UI;
  • datahub-mae-consumer сервис для обработки сообщений Metadata Audit Events;
  • datahub-mce-consumer сервис для обработки сообщений Metadata Change Events;
  • mysql база данных для хранения метаданных.


Основная сущность DataHub dataset. Он может включать в себя таблицы (RDBMS и не только), топики в Kafka, директории на HDFS или другие сущности, имеющие схему.


Датасет имеет:


  • схему (включая типы и комментарии к полям),
  • статус (active или deprecated),
  • владельцев,
  • relationships (он же lineage),
  • docs с указанием ссылок на документацию.

Метаданные обновляются через отправку сообщений Metadata Change Event (MCE) в Kafka. MCE это сообщение в формате AVRO с указанием пунктов, которые необходимо обновить. Гибкость обновления данных в системе достигается за счет возможности в одном сообщении обновить владельцев датасета, в другом обновить схему, в третьем upstream datasets.


Отличительная особенность DataHub приятный веб-интерфейс. Он нам сразу понравился и запал в душу. У него все хорошо в плане поиска, обновлений типов таблиц и типов датасетов, информация о схеме датасета выглядит очень приятно. Можно добавлять владельцев датасетов, можно зайти в профиль пользователя и посмотреть, какими датасетами он владеет. У DataHub есть lineage, для каждого датасета можно наблюдать его взаимосвязи с другими объектами. Также есть возможность к каждому датасету прикладывать ссылки на документацию или исходный код.


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


Плюсы:


  • удобный UI с поиском;
  • автоматический сбор данных из разных СУБД (большая гибкость, поддерживает сбор данных не только из СУБД, работает для всего, у чего есть схема);
  • добавление или редактирование данных в автоматическом режиме через отправку AVRO-сообщений в Kafka;
  • добавление ссылок на документацию к датасету;
  • визуализация data lineage от источника до отчета в BI-системе (однако нет возможности отобразить всю цепочку сразу, отображается только upstream и downstream датасеты на один уровень вверх и вниз);
  • отображение data owner;
  • есть возможность сделать связку с интранетом компании.

Минусы:


  • огромное количество внутренних сервисов, за каждым из которых нужно следить;
  • отсутствует трекинг изменения датасетов;
  • data lineage показывает только upstream и downstream датасеты;
  • отсутствие визуализации data profiling;
  • отсутствие визуализации data quality (в roadmap на Q2 2021 есть пункт про отображение визуализаций и интеграцию с такими системами, как Great Expectations и deequ);
  • нет возможности добавить кастомную информацию для датасета;
  • нет возможности прослеживать изменения в датасетах;
  • поиск работает только для датасетов и пользователей.

Marquez



Третий инструмент Marquez. Он состоит из основного приложения, базы данных и веб-интерфейса для отображения датасетов, джобов и связей между ними.


Метаданные в Marquez отправляются с помощью REST API. Еще он поддерживает создание следующих типов объектов:


  • data source системы-источники;
  • dataset таблицы, из которых читаются и в которые пишутся данные, обрабатываемые джобами;
  • job абстракция над процессом трансформации данных, которая принимает таблицы на вход и записывают в них данные;
  • job run запуск конкретной джобы.


Marquez на самом деле очень простой и не имеет в себе ничего лишнего. У него хорошая модель данных: абстракции, которые заложили в него разработчики, позволяют довольно полно описывать процессы обработки и трансформации данных.


Его самый главный минус слишком минималистичный интерфейс, он плохо справляется с отображением lineage, в котором есть много таблиц и ветвлений. Нет возможности отображать владельца данных, нельзя в режиме справочника посмотреть, какие таблицы у нас есть. Нет возможности отображать информацию по качеству данных, по профилированию, невозможно добавить кастомную информацию. То есть Marquez максимально простой инструмент, который может подойти для каких-то простых use-caseов, но не подойдет для чего-то масштабного.


Плюсы:


  • быстрый и минималистичный UI;
  • поддержка airflow из коробки;
  • простая, но гибкая модель данных, позволяет с минимальным набором абстракций описывать данные;
  • понятный и простой API для добавления или редактирования данных;
  • Web UI с поиском;
  • есть lineage;
  • минимум компонентов.

Минусы:


  • слишком минималистичный UI;
  • отсутствует авторизация;
  • плохо работает в режиме пробежаться глазами и посмотреть, какие данные вообще есть;
  • не отображается data owner;
  • поиск работает только для датасетов и джобов;
  • нет возможности прослеживать изменения в датасетах;
  • отсутствует трекинг изменения датасетов;
  • отсутствует визуализация data profiling;
  • отсутствует визуализация data quality;
  • нет возможности добавить кастомную информацию для датасета.

Бонус: загоняем lineage из DWH в Neo4j


В качестве бонуса мы решили попробовать графовую базу данных Neo4j для отображения lineage. Источником данных стала сервисная таблица в нашем хранилище, в которой для каждой другой таблицы указано, какие объекты участвовали в ее формировании. Мы взяли три самых массивных представления и прошлись по их lineage вплоть до систем-источников.



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


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



Во втором подходе мы попробовали убрать джобы и просто связать таблицы между собой. Вершин в графе стало очевидно меньше, однако читать его легче не стало.
После этого мы загнали данные из Neo4j в инструмент под названием neo4j-explorer, который создан для более структурированного отображения графа из Neo4j.



Зеленые блоки джобы, серые таблицы. Можно выделить джоб или таблицу и подсветить его зависимости в обе стороны. Несмотря на то, что выглядит это мощно (и напоминает кусок производства из игры Factorio), ничего полезного из этого мы вынести тоже не можем.


Что мы выбрали в итоге и почему не стали внедрять


В результате нашим фаворитом стал LinkedIn DataHub. Но мы поняли, что большинство текущих хотелок полностью покрываются Confluence, а у команд аналитиков сложились устоявшиеся процессы по работе с данными. Внедрять новую сложную систему и изменять текущие подходы к работе стоит только ради очень серьезных улучшений. Помимо этого, плюсы систем и их ограничения не перевешивают для нас трудоемкости внедрения и перехода.


Проведя Customer Development среди потенциальных пользователей, мы пришли к выводу, что ни одна из систем не поможет сэкономить рабочее время тех людей, которые работают с данными. При этом сложность внедрения и перестройки процессов будет существенной. Поэтому мы решили на какое-то время отложить выбор.


Мы отслеживаем развитие рассмотренных в статье сервисов, изучаем платные варианты Data Catalog и их возможности. Если у вас есть успешный (или не очень) опыт внедрения подобных систем, то поделитесь им в комментариях.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru