Школа
системного анализа
и проектирования
ДЖО РИС | МЭТТ ХОУСЛИ

Книга «Основы инженерии данных» (Fundamentals of Data Engineering)
Планирование и построение надёжных систем данных

2022 / 2024


Оглавление книги
Глава 5

Генерация данных в исходных системах

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

В этой главе мы рассмотрим некоторые популярные операционные паттерны и значимые типы исходных систем. Существует множество исходных систем для генерации данных, и мы не будем охватывать их все. Мы рассмотрим данные, которые генерируют эти системы, и аспекты, которые следует учитывать при работе с исходными системами. Мы также обсудим, как фоновые процессы инженерии данных применяются к этому первому этапу жизненного цикла инженерии данных (Рисунок 5−1).
Рисунок 5−1. Исходные системы генерируют данные для остальных этапов жизненного цикла инженерии данных
По мере того как данные становятся всё более распространёнными, растёт объём обмена данными (об этом пойдет речь далее), ожидается, что роль инженера данных значительно изменится в сторону понимания взаимодействия между источниками и назначениями данных. Основные задачи по перемещению данных из точки, А в точку Б упростятся. В то же время, останется критически важным понимать природу данных, создаваемых в исходных системах.

Источники данных: Как создаются данные?

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

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

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

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

Ознакомьтесь с вашей исходной системой и тем, как она генерирует данные. Потратьте время на чтение документации исходной системы и понимание её паттернов и особенностей. Если ваша исходная система — это реляционная система управления базами данных (RDBMS), узнайте, как она работает (записывает, фиксирует, запрашивает и т. д.); изучите все аспекты исходной системы, которые могут повлиять на вашу способность получать данные из неё.

Исходные системы: основные идеи

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

Файлы и неструктурированные данные

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

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

Основные типы исходных форматов файлов, с которыми вы столкнётесь как инженер данных — файлы, которые создаются либо вручную, либо в результате процесса исходной системы — это Excel, CSV, TXT, JSON и XML. Эти файлы имеют свои особенности и могут быть структурированными (Excel, CSV), полуструктурированными (JSON, XML, CSV) или неструктурированными (TXT, CSV).

Хотя вы как инженер данных будете активно использовать определённые форматы (такие как Parquet, ORC и Avro), мы рассмотрим их позже и здесь сосредоточим внимание на файлах исходных систем. Глава 6 охватывает технические детали файлов.

API

Application programming interfaces (API) являются стандартным способом обмена данными между системами. В теории API упрощают задачу получения данных для инженеров данных. На практике многие API всё ещё предъявляют значительную сложность данных, с которой инженерам приходится управляться. Даже с появлением различных сервисов и фреймворков, а также сервисов для автоматизации получения данных через API, инженеры данных часто должны прикладывать значительные усилия для поддержки пользовательских соединений API. Мы рассмотрим API более подробно позже в этой главе.

Базы данных приложений (OLTP-системы)

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

Обычно база данных приложения является системой онлайн-транзакционной обработки (online transaction processing — OLTP) — базой данных, которая читает и записывает отдельные записи данных с высокой скоростью. OLTP-системы часто называют транзакционными базами данных, но это не обязательно означает, что система поддерживает атомарные транзакции.

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

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

ACID

Поддержка атомарных транзакций является одной из критически важных характеристик базы данных, известных вместе как ACID (как вы, возможно, помните из главы 3, это означает атомарность, согласованность, изоляцию и долговечность). Согласованность означает, что любое чтение из базы данных вернёт последнюю записанную версию извлечённого элемента. Изоляция подразумевает, что если два обновления выполняются одновременно для одного и того же элемента, конечное состояние базы данных будет согласовано с последовательным выполнением этих обновлений в порядке их отправки. Долговечность указывает на то, что зафиксированные данные никогда не будут потеряны, даже в случае отключения питания.

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

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

Атомарные транзакции

Атомарная транзакция (atomic transaction) — это набор нескольких изменений, которые фиксируются как единое целое. В примере на рисунке 5−2 традиционное банковское приложение, работающее на реляционной системе управления базами данных (RDBMS), выполняет SQL-запрос, который проверяет балансы двух счетов: одного на счёте, А (источник) и другого на счёте Б (получатель). Деньги затем переводятся со счёта, А на счёт Б, если на счёте, А достаточно средств. Вся транзакция должна выполняться с обновлением балансов обоих счетов или не выполняться вовсе, не обновляя ни один из счетов. То есть вся операция должна происходить как одна транзакция.
Рисунок 5-2. Пример атомарной транзакции: банковский перевод с использованием OLTP.

OLTP и аналитика

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

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

OLAP-система

В отличие от OLTP-системы, система интерактивной аналитической обработки (online analytical processing — OLAP) создана для выполнения крупных аналитических запросов и обычно неэффективна при обработке поиска отдельных записей. Например, современные колоночные базы данных оптимизированы для сканирования больших объёмов данных, отказываясь от индексов для улучшения масштабируемости и производительности сканирования. Любой запрос обычно включает сканирование минимального блока данных, часто размером 100 МБ или более. Попытка выполнить тысячи поисковых запросов в секунду в такой системе приведёт к её сбою, если она не будет комбинироваться с кэширующим слоем, предназначенным для этого случая использования.

Обратите внимание, что мы используем термин OLAP для обозначения любой системы базы данных, которая поддерживает высокомасштабируемые интерактивные аналитические запросы; мы не ограничиваемся системами, поддерживающими OLAP-кубы (многомерные массивы данных). Онлайн-часть OLAP подразумевает, что система постоянно прослушивает входящие запросы, делая OLAP-системы подходящими для интерактивной аналитики.
Хотя эта глава охватывает исходные системы, OLAP-системы обычно являются системами хранения и запросов для аналитики. Почему мы говорим о них в нашей главе об исходных системах? В практических случаях использования инженерам часто нужно читать данные из OLAP-системы. Например, хранилище данных может предоставлять данные, используемые для обучения модели машинного обучения. Или OLAP-система может обслуживать обратный ETL-процесс, где производные данные в аналитической системе отправляются обратно в исходную систему, такую как CRM, SaaS-платформа или транзакционное приложение.

Захват изменения данных

Захват изменений данных (change data capture — CDC) — это метод извлечения каждого события изменения (вставки, обновления, удаления), происходящего в базе данных. CDC часто используется для репликации между базами данных в режиме реального времени или для создания потока событий для последующей обработки.

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

Журналы

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

■ Операционные системы
■ Приложения
■ Серверы
■ Контейнеры
■ Сети
■ Устройства Интернета вещей (IoT)
Все журналы отслеживают события и метаданные событий. Как минимум, журнал должен фиксировать, кто, что и когда:

Кто
Человек, система или учётная запись сервиса, связанная с событием (например, пользовательский агент веб-браузера или идентификатор пользователя)

Что произошло
Событие и связанные с ним метаданные

Когда
Временная метка события

Кодирование журнала

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

Бинарно-кодированные журналы
Эти журналы кодируют данные в компактном пользовательском формате для эффективного использования пространства и быстрого ввода-вывода. Простым примером являются журналы баз данных, обсуждаемые в разделе «Журналы баз данных».

Полуструктурированные журналы
Эти журналы кодируются в текстовом формате сериализации объектов (чаще всего JSON). Полуструктурированные журналы машиночитаемы и переносимы. Однако они гораздо менее эффективны, чем бинарные журналы. И хотя они номинально машиночитаемы, извлечение ценности из них часто требует значительного количества пользовательского кода.

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

Разрешение журнала

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

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

Задержка журналирования: пакетный режим или режим реального времени

Пакетные журналы часто записываются непрерывно в файл. Отдельные записи журнала могут быть записаны в брокер сообщений, такой как Kafka или Pulsar, для приложений реального времени.

Журналы базы данных

Журналы базы данных настолько важны, что заслуживают более детального освещения. Журналы предварительной записи — обычно бинарные файлы, хранящиеся в специфическом формате, родном для базы данных — играют ключевую роль в гарантиях базы данных и её восстанавливаемости. Сервер базы данных получает запросы на запись и обновление в таблицу базы данных (см. Рисунок 5-3), сохраняя каждую операцию в журнал перед подтверждением запроса. Подтверждение сопровождается гарантией, связанной с журналом: даже если сервер выйдет из строя, он сможет восстановить своё состояние при перезагрузке, завершив незавершённую работу из журналов.

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

CRUD

CRUD (create, read, update, delete), что означает создание, чтение, обновление и удаление, является транзакционным шаблоном, широко используемым в программировании, и представляет четыре основные операции постоянного хранения. CRUD является наиболее распространённым шаблоном для хранения состояния приложения в базе данных. Основной принцип CRUD заключается в том, что данные должны быть созданы перед использованием. После создания данных их можно читать и обновлять. В конечном итоге данные могут потребоваться для удаления. CRUD гарантирует, что эти четыре операции будут выполнены над данными, независимо от их хранения.

CRUD является широко используемым шаблоном в программных приложениях, и вы часто найдёте CRUD в API и базах данных. Например, веб-приложение будет активно использовать CRUD для RESTful HTTP-запросов и хранения и извлечения данных из базы данных.

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

Только вставка

Шаблон «только вставка» сохраняет историю непосредственно в таблице, содержащей данные. Вместо обновления записей вставляются новые записи с меткой времени, указывающей, когда они были созданы (Таблица 5-1). Например, предположим, у вас есть таблица с адресами клиентов. Следуя шаблону CRUD, вы бы просто обновили запись, если клиент изменил свой адрес. С шаблоном «только вставка» вставляется новая запись адреса с тем же идентификатором клиента. Чтобы прочитать текущий адрес клиента по идентификатору клиента, вы бы искали последнюю запись под этим идентификатором.
Таблица 5-1. Шаблон «только вставка» создаёт несколько версий записи
В некотором смысле, шаблон «только вставка» поддерживает журнал базы данных непосредственно в самой таблице, что делает его особенно полезным, если приложению требуется доступ к истории. Например, шаблон «только вставка» хорошо подошёл бы для банковского приложения, предназначенного для представления истории адресов клиентов.

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

Шаблон «только вставка» имеет несколько недостатков. Во-первых, таблицы могут значительно увеличиваться в размере, особенно если данные часто изменяются, так как каждое изменение вставляется в таблицу. Иногда записи удаляются на основе даты истечения срока действия записи или максимального количества версий записи, чтобы поддерживать разумный размер таблицы. Второй недостаток заключается в том, что поиск записей требует дополнительных накладных расходов, поскольку поиск текущего состояния включает выполнение операции MAX(created_timestamp). Если под одним идентификатором находятся сотни или тысячи записей, эта операция поиска обходится дорого.

Сообщения и потоки

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

Сообщение — это сырые данные, передаваемые между двумя или более системами (Рисунок 5−4). Например, у нас есть Система 1 и Система 2, где Система 1 отправляет сообщение Системе 2. Эти системы могут быть разными микросервисами, сервером, отправляющим сообщение бессерверной функции, и т. д. Сообщение обычно отправляется через очередь сообщений от издателя к потребителю, и после доставки сообщение удаляется из очереди.
Рисунок 5-4. Сообщение, передаваемое между двумя системами
Сообщения — это дискретные и единичные сигналы в системе, ориентированной на события. Например, устройство Интернета вещей (IoT) может отправить сообщение с последним показателем температуры в очередь сообщений. Это сообщение затем обрабатывается службой, которая определяет, следует ли включить или выключить котёл. Эта служба отправляет сообщение контроллеру котла, который выполняет соответствующее действие. После получения сообщения и выполнения действия сообщение удаляется из очереди сообщений.

В отличие от этого, поток — это добавляемый только в конец журнал записей событий. (Потоки обрабатываются и хранятся на платформах потоковой передачи событий, о которых мы поговорим подробнее в разделе «Сообщения и потоки» на странице 163.) По мере возникновения событий они накапливаются в упорядоченной последовательности (Рисунок 5-5); события могут быть упорядочены по временной метке или идентификатору. (Обратите внимание, что события не всегда доставляются в точном порядке из-за тонкостей распределённых систем.)
Рисунок 5-5. Поток, который представляет собой упорядоченный добавляемый только в конец журнал записей

Типы времени

Хотя время является важным аспектом для всех процессов поглощения данных, оно становится ещё более критичным и тонким в контексте стриминга, где данные рассматриваются как непрерывные и ожидается их потребление сразу после создания. Давайте рассмотрим ключевые типы времени, с которыми вы столкнётесь при поглощении данных: время генерации события, время его поглощения и обработки, а также время, затраченное на обработку (Рисунок 5-6).
Рисунок 5-6. Время события, поглощения, обработки и выполнения обработки
Время события (event time) указывает, когда событие было сгенерировано в исходной системе, включая временную метку самого события. После создания события происходит неопределённая временная задержка до того, как событие будет поглощено и обработано дальше по цепочке. Всегда включайте временные метки для каждой фазы, через которую проходит событие. Регистрируйте события по мере их возникновения и на каждом этапе времени — когда они создаются, поглощаются и обрабатываются. Используйте эти журналы временных меток, чтобы точно отслеживать движение ваших данных через конвейеры данных.

После создания данные поглощаются где-то. Время поглощения (ingestion time) указывает, когда событие поглощается из исходных систем в очередь сообщений, кэш, память, объектное хранилище, базу данных или любое другое место, где хранятся данные (см. Главу 6). После поглощения данные могут быть обработаны немедленно; или в течение минут, часов или дней; или просто храниться неопределённо долго.

Время обработки (process time) наступает после времени поглощения, когда данные обрабатываются (обычно это трансформация). Время выполнения обработки (processing time) — это время, затраченное на обработку данных, измеряемое в секундах, минутах, часах и т.д.

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

Практические сведения об исходных системах

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

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

Базы данных

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

Основные соображения для понимания технологий баз данных

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

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

Поиск
Как база данных находит и извлекает данные? Индексы могут помочь ускорить поиск, но не все базы данных имеют индексы. Знайте, использует ли ваша база данных индексы; если да, то какие лучшие шаблоны для их проектирования и поддержки? Понимайте, как использовать их для эффективного извлечения. Также полезно иметь базовые знания о основных типах индексов, включая B-деревья и лог-структурированные деревья слияния (LSM).

Оптимизатор запросов
Использует ли база данных оптимизатор? Каковы его характеристики?

Масштабирование и распределение
Масштабируется ли база данных с ростом спроса? Какую стратегию масштабирования она использует? Масштабируется ли она горизонтально (больше узлов базы данных) или вертикально (больше ресурсов на одном компьютере)?

Шаблоны моделирования
Какие шаблоны моделирования лучше всего работают с базой данных (например, нормализация данных или широкие таблицы)? (См. Главу 8 для нашего обсуждения моделирования данных.)

CRUD
Как данные запрашиваются, создаются, обновляются и удаляются в базе данных? Каждый тип базы данных обрабатывает операции CRUD по-разному.

Согласованность
Является ли база данных полностью согласованной, или она поддерживает модель расслабленной согласованности (например, конечная согласованность)? Поддерживает ли база данных необязательные режимы согласованности для чтения и записи (например, сильно согласованные чтения)?
Мы делим базы данных на реляционные и нереляционные. На самом деле, категория нереляционных баз данных гораздо более разнообразна, но реляционные базы данных всё ещё занимают значительное место в бэкендах приложений.

Реляционные базы данных

Система управления реляционными базами данных (СУРБД) является одним из наиболее распространённых бэкендов приложений. Реляционные базы данных были разработаны в IBM в 1970-х годах и популяризированы Oracle в 1980-х. Рост популярности Интернета привёл к появлению стека LAMP (Linux, веб-сервер Apache, MySQL, PHP) и взрывному росту различных коммерческих и открытых СУРБД. Даже с появлением NoSQL баз данных (описанных в следующем разделе), реляционные базы данных остаются чрезвычайно популярными.

Данные хранятся в таблице отношений (строк), и каждое отношение содержит несколько полей (колонок); см. Рисунок 5−7. Обратите внимание, что в этой книге мы используем термины «колонка» и «поле» как синонимы. Каждое отношение в таблице имеет одну и ту же схему (последовательность колонок с назначенными статическими типами, такими как строка, целое число или число с плавающей запятой). Строки обычно хранятся как непрерывная последовательность байтов на диске.
Рисунок 5−7. СУРБД хранит и извлекает данные на уровне строк
Полное обсуждение теории, истории и технологий СУРБД выходит за рамки этой книги. Мы рекомендуем вам изучить системы СУРБД, реляционную алгебру и стратегии нормализации, так как они широко распространены, и вы будете часто с ними сталкиваться. См. раздел «Дополнительные ресурсы».

Нереляционные базы данных: NoSQL

Хотя реляционные базы данных отлично подходят для многих случаев использования, они не являются универсальным решением. Часто мы видим, что люди начинают с реляционной базы данных, считая её универсальным инструментом, и пытаются втиснуть в неё множество случаев использования и нагрузок. По мере изменения требований к данным и запросам, реляционная база данных не выдерживает нагрузки. В этот момент вам захочется использовать базу данных, подходящую для конкретной нагрузки. Здесь на сцену выходят нереляционные или NoSQL базы данных. NoSQL, что означает «не только SQL», относится к целому классу баз данных, которые отказываются от реляционного парадигмы.

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

Одной из ключевых тем этой книги является то, что инновации в области данных постоянны. Давайте быстро взглянем на историю NoSQL, так как это поможет понять, как и почему инновации в области данных влияют на вашу работу как инженера данных. В начале 2000-х годов такие технологические компании, как Google и Amazon, начали перерастать свои реляционные базы данных и разработали новые распределённые нереляционные базы данных для масштабирования своих веб-платформ.

Хотя термин NoSQL впервые появился в 1998 году, его современная версия была предложена Эриком Эвансом в 2000-х годах.

Он рассказывает эту историю в посте в 2009 году:
Я провёл последние несколько дней на nosqleast, и одной из горячих тем здесь было «NoSQL». Понятно, что многие люди беспокоятся о том, что название плохое, что оно передаёт неправильное или неточное сообщение. Хотя я не претендую на авторство этой идеи, я должен признать свою вину за то, как оно теперь называется. Как так? Йохан Оскарссон организовывал первую встречу и спросил в IRC: «Какое хорошее название?» Это было одно из трёх или четырёх предложений, которые я выпалил за 45 секунд, не задумываясь.

Моё сожаление, однако, связано не с тем, что говорит название, а с тем, что оно не говорит. Когда Йохан впервые придумал идею первой встречи, он, казалось, думал о больших данных и линейно масштабируемых распределённых системах, но название настолько расплывчатое, что открыло дверь для предложений докладов на любую тему, связанную с хранением данных, которая не была СУРБД.
Понятие NoSQL остаётся расплывчатым и в 2022 году, но оно было широко принято для описания вселенной «новых» баз данных, альтернатив реляционным базам данных.

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

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

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

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

Хранилище документов. Как упоминалось ранее, хранилище документов — это специализированное хранилище ключ-значение. В этом контексте документ — это вложенный объект; обычно мы можем рассматривать каждый документ как JSON-объект для практических целей. Документы хранятся в коллекциях и извлекаются по ключу. Коллекция примерно эквивалентна таблице в реляционной базе данных (см. Таблицу 5-2).
Таблица 5-2. Сравнение терминологии СУРБД и документных баз данных
Одно из ключевых различий между реляционными базами данных и документными хранилищами заключается в том, что последние не поддерживают соединения. Это означает, что данные не могут быть легко нормализованы, то есть разделены на несколько таблиц. (Приложения всё ещё могут выполнять соединения вручную. Код может искать документ, извлекать свойство, а затем извлекать другой документ.) Идеально, если все связанные данные могут быть сохранены в одном и том же документе.

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

Документные базы данных обычно принимают всю гибкость JSON и не применяют схемы или типы; это одновременно и благо, и проклятие. С одной стороны, это позволяет схеме быть высоко гибкой и выразительной. Схема также может эволюционировать по мере роста приложения. С другой стороны, мы видели, как документные базы данных становятся абсолютным кошмаром для управления и запросов. Если разработчики не будут осторожны в управлении эволюцией схемы, данные могут стать несогласованными и раздутыми со временем. Эволюция схемы также может нарушить поглощение данных ниже по цепочке и вызвать головную боль у инженеров данных, если она не будет своевременно сообщена (до развёртывания).

Ниже приведён пример данных, хранящихся в коллекции под названием users. Ключом коллекции является id. У нас также есть имя (с дочерними элементами first и last) и массив любимых групп пользователя в каждом документе:
{
  "users":[
      {
      "id":1234,
      "name":{
      "first":"Joe",
      "last":"Reis"
      },
      "favorite_bands":[
      "AC/DC",
      "Slayer",
      "WuTang Clan",
      "Action Bronson"
      ]
      },
      {
      "id":1235,
      "name":{
      "first":"Matt",
      "last":"Housley"
      },
      "favorite_bands":[
      "Dave Matthews Band",
      "Creed",
      "Nickelback"
      ]
      }
  ]
}
Для запроса данных в этом примере можно извлекать записи по ключу. Обратите внимание, что большинство документных баз данных также поддерживают создание индексов и таблиц поиска, что позволяет извлекать документы по конкретным свойствам. Это часто бесценно в разработке приложений, когда нужно искать документы различными способами. Например, можно установить индекс на имя.

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

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

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

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

Эти базы данных поддерживают быстрое сканирование огромных объёмов данных, но не поддерживают сложные запросы. У них есть только один индекс (ключ строки) для поиска. Инженерам данных обычно приходится извлекать данные и отправлять их во вторичную аналитическую систему для выполнения сложных запросов, чтобы справиться с этими ограничениями. Это можно сделать, выполняя большие сканирования для извлечения данных или используя CDC для захвата потока событий.

Графовые базы данных. Графовые базы данных явно хранят данные с математической структурой графа (в виде набора узлов и рёбер).

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

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

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

Графовые базы данных разработаны именно для такого типа запросов. Их структуры данных позволяют выполнять запросы на основе связей между элементами; графовые базы данных показаны, когда нам важно понимать сложные траверсы между элементами. В терминологии графов мы храним узлы (пользователи в предыдущем примере) и рёбра (связи между пользователями). Графовые базы данных поддерживают богатые модели данных как для узлов, так и для рёбер. В зависимости от используемого движка графовой базы данных, графовые базы данных используют специализированные языки запросов, такие как SPARQL, Resource Description Framework (RDF), Graph Query Language (GQL) и Cypher.

В качестве примера графа рассмотрим сеть из четырёх пользователей. Пользователь 1 подписан на Пользователя 2, который подписан на Пользователя 3 и Пользователя 4; Пользователь 3 также подписан на Пользователя 4 (Рисунок 5-8).
Рисунок 5-8. Граф социальной сети
Мы предполагаем, что приложения графовых баз данных будут быстро расти за пределами технологических компаний; анализы рынка также прогнозируют быстрый рост. Конечно, графовые базы данных полезны с операционной точки зрения и поддерживают сложные социальные отношения, критически важные для современных приложений. Графовые структуры также увлекательны с точки зрения науки о данных и машинного обучения, потенциально раскрывая глубокие инсайты в человеческие взаимодействия и поведение.
Это вводит уникальные вызовы для инженеров данных, которые могут быть более привычны к работе со структурированными отношениями, документами или неструктурированными данными. Инженеры должны решить, что делать дальше:

■ Отобразить данные графовой исходной системы в одну из их существующих предпочтительных парадигм
■ Анализировать графовые данные в самой исходной системе
■ Применить специализированные аналитические инструменты для графов
Данные графа могут быть перекодированы в строки в реляционной базе данных, что может быть подходящим решением в зависимости от аналитического случая использования. Транзакционные графовые базы данных также предназначены для аналитики, хотя большие запросы могут перегружать производственные системы. Современные облачные графовые базы данных поддерживают аналитику графов с интенсивным чтением на огромных объёмах данных.

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

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

В зависимости от типа компании, в которой вы работаете, вы можете использовать поисковые базы данных либо регулярно, либо вообще не использовать. В любом случае, полезно знать о их существовании, если вы столкнётесь с ними в реальной жизни. Поисковые базы данных популярны для быстрого поиска и извлечения данных и могут быть найдены в различных приложениях; сайт электронной коммерции может использовать поисковую базу данных для поиска продуктов. Как инженер данных, от вас может потребоваться перенос данных из поисковой базы данных (такой как Elasticsearch, Apache Solr или Lucene, или Algolia) в отчёты по ключевым показателям эффективности или что-то подобное.

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

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

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

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

API

API сейчас является стандартным и повсеместным способом обмена данными в облаке, для платформ SaaS и между внутренними системами компаний. Существует множество типов API-интерфейсов в интернете, но нас в первую очередь интересуют те, которые построены вокруг HTTP, самого популярного типа в интернете и облаке.

REST

Сначала мы поговорим о REST, который в настоящее время является доминирующей парадигмой API. Как упоминалось в Главе 4, REST означает representational state transfer (передача состояния представления). Этот набор практик и философий для создания HTTP веб-API был описан Роем Филдингом в 2000 году в его докторской диссертации. REST построен вокруг HTTP-глаголов, таких как GET и PUT; на практике современный REST использует лишь небольшое количество глаголов, описанных в оригинальной диссертации.

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

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

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

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

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

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

GraphQL

GraphQL был создан в Facebook как язык запросов для данных приложений и альтернатива общим REST API. В то время как REST API обычно ограничивают ваши запросы определённой моделью данных, GraphQL открывает возможность извлечения нескольких моделей данных в одном запросе. Это позволяет делать более гибкие и выразительные запросы, чем с REST. GraphQL построен вокруг JSON и возвращает данные в форме, напоминающей JSON-запрос.

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

Вебхуки

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

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

RPC и gRPC

Удалённый вызов процедур (remote procedure call — RPC) часто используется в распределённых вычислениях. Он позволяет выполнять процедуру на удалённой системе.
gRPC — это библиотека удалённого вызова процедур, разработанная внутри Google в 2015 году и позже выпущенная как открытый стандарт. Её использование в Google само по себе достаточно значимо, чтобы включить её в наше обсуждение. Многие сервисы Google, такие как Google Ads и GCP, предлагают gRPC API. gRPC построен вокруг открытого стандарта сериализации данных Protocol Buffers, также разработанного Google.

gRPC подчёркивает эффективный двусторонний обмен данными по HTTP/2. Эффективность относится к таким аспектам, как использование CPU, потребление энергии, время работы батареи и пропускная способность. Как и GraphQL, gRPC накладывает гораздо более конкретные технические стандарты, чем REST, что позволяет использовать общие клиентские библиотеки и позволяет инженерам развивать навыки, которые будут применимы к любому коду взаимодействия gRPC.

Совместное использование данных

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

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

Обмен данными также может упростить конвейеры данных внутри организации. Обмен данными позволяет подразделениям организации управлять своими данными и выборочно делиться ими с другими подразделениями, при этом позволяя отдельным подразделениям управлять своими вычислительными и запросными затратами отдельно, что способствует децентрализации данных. Это облегчает децентрализованные паттерны управления данными, такие как сетка данных (data mesh).

Обмен данными и сетка данных тесно соответствуют нашей философии общих архитектурных компонентов. Выбирайте общие компоненты (см. Главу 3), которые позволяют простой и эффективный обмен данными и знаниями, а не принимайте самые захватывающие и сложные технологии.

Сторонние источники данных

Консьюмеризация технологий означает, что каждая компания теперь по сути является технологической компанией. Результатом этого является то, что эти компании — и всё больше государственные учреждения — хотят делать свои данные доступными для своих клиентов и пользователей, либо как часть их услуги, либо как отдельную подписку. Например, Бюро статистики труда США публикует различные статистические данные о рынке труда США. Национальное управление по аэронавтике и исследованию космического пространства (NASA) публикует различные данные из своих исследовательских инициатив. Facebook делится данными с бизнесами, которые рекламируются на его платформе.

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

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

Очереди сообщений и платформы потоковой передачи событий

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

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

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

Очереди сообщений

Очередь сообщений — это механизм для асинхронной отправки данных (обычно в виде небольших отдельных сообщений, размером в килобайты) между дискретными системами с использованием модели публикации и подписки. Данные публикуются в очередь сообщений и доставляются одному или нескольким подписчикам (Рисунок 5-9). Подписчик подтверждает получение сообщения, удаляя его из очереди.
Рисунок 5-9. Простая очередь сообщений
Очереди сообщений позволяют приложениям и системам быть отсоединёнными друг от друга и широко используются в архитектурах микросервисов. Очередь сообщений буферизует сообщения для обработки временных пиков нагрузки и делает сообщения устойчивыми за счёт распределённой архитектуры с репликацией.

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

Порядок сообщений и доставка. Порядок, в котором сообщения создаются, отправляются и получаются, может значительно влиять на нижестоящих подписчиков. В общем, порядок в распределённых очередях сообщений — это сложная проблема. Очереди сообщений часто применяют нечёткое представление о порядке и принципе "первым пришёл — первым обслужен" (FIFO). Строгий FIFO означает, что если сообщение A поглощено раньше сообщения B, то сообщение A всегда будет доставлено раньше сообщения B. На практике сообщения могут быть опубликованы и получены в неправильном порядке, особенно в высокораспределённых системах сообщений.

Например, стандартные очереди Amazon SQS делают всё возможное для сохранения порядка сообщений. SQS также предлагает FIFO-очереди, которые обеспечивают гораздо более строгие гарантии, но за счёт дополнительных накладных расходов.

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

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

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

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

Платформы потоковой передачи событий

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

Давайте опишем событие, связанное с платформой потоковой передачи событий. Как упоминалось в Главе 3, событие — это "что-то, что произошло, обычно изменение состояния чего-либо". Событие имеет следующие характеристики: ключ, значение и временную метку. В одном событии могут содержаться несколько пар "ключ-значение-временная метка". Например, событие для заказа в электронной коммерции может выглядеть следующим образом:
{
    "Key":"Order # 12345",
    "Value":"SKU 123, purchase price of $100",
    "Timestamp":"2023-01-02 06:01:00"
}
Давайте рассмотрим некоторые критические характеристики платформы потоковой передачи событий, о которых вам следует знать как инженеру данных.

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

Используя предыдущий пример события, топик может быть веб-заказами. Также давайте отправим этот топик нескольким потребителям, таким как выполнение заказов и маркетинг. Это отличный пример размытых границ между аналитикой и системой, ориентированной на события. Подписчик на выполнение заказов будет использовать события для запуска процесса выполнения, в то время как маркетинг будет запускать аналитику в реальном времени или обучать и запускать модели машинного обучения для настройки маркетинговых кампаний (Рисунок 5-10).
Рисунок 5-10. Система обработки заказов генерирует события (маленькие квадраты) и публикует их в топик web orders. Два подписчика — marketing и fulfillment — извлекают события из топика.
Разделы потоков. Разделы потоков представляют собой подразделения потока на несколько потоков. Хорошей аналогией является многополосное шоссе. Наличие нескольких полос позволяет использовать параллелизм и более высокую пропускную способность. Сообщения распределяются по разделам по ключу раздела. Сообщения с одинаковым ключом раздела всегда попадают в один и тот же раздел.
На рисунке 5-11, например, каждое сообщение имеет числовой идентификатор — показан внутри круга, представляющего сообщение — который мы используем в качестве ключа раздела. Чтобы определить раздел, мы делим на 3 и берём остаток. Идя снизу вверх, разделы имеют остатки 0, 1 и 2 соответственно.

Рисунок 5-10. Система обработки заказов генерирует события (маленькие квадраты) и публикует их в топик web orders. Два подписчика — marketing и fulfillment — извлекают события из топика.
Разделы потоков. Разделы потоков представляют собой подразделения потока на несколько потоков. Хорошей аналогией является многополосное шоссе. Наличие нескольких полос позволяет использовать параллелизм и более высокую пропускную способность. Сообщения распределяются по разделам по ключу раздела. Сообщения с одинаковым ключом раздела всегда попадают в один и тот же раздел.

На рисунке 5-11, например, каждое сообщение имеет числовой идентификатор — показан внутри круга, представляющего сообщение — который мы используем в качестве ключа раздела. Чтобы определить раздел, мы делим на 3 и берём остаток. Идя снизу вверх, разделы имеют остатки 0, 1 и 2 соответственно.
Рисунок 5-11. Входящий поток сообщений, разделённый на три раздела
Установите ключ раздела так, чтобы сообщения, которые должны обрабатываться вместе, имели одинаковый ключ раздела. Например, в настройках IoT часто требуется отправлять все сообщения от конкретного устройства на один и тот же сервер обработки. Мы можем достичь этого, используя идентификатор устройства в качестве ключа раздела, а затем настроив один сервер для потребления из каждого раздела.

Основная проблема с разделением потоков заключается в том, чтобы убедиться, что ваш ключ раздела не создаёт горячих точек — непропорционального количества сообщений, доставляемых в один раздел. Например, если бы каждое устройство IoT было известно как находящееся в определённом штате США, мы могли бы использовать штат в качестве ключа раздела. При распределении устройств пропорционально населению штата, разделы, содержащие Калифорнию, Техас, Флориду и Нью-Йорк, могли бы быть перегружены, в то время как другие разделы были бы относительно недоиспользованы. Убедитесь, что ваш ключ раздела будет распределять сообщения равномерно по разделам.

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

С кем вы будете работать

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

Кто эти стейкхолдеры? Обычно вы будете иметь дело с двумя категориями стейкхолдеров: системными и данными стейкхолдерами (Рисунок 5-12). Системные стейкхолдеры создают и поддерживают исходные системы; это могут быть инженеры-программисты, разработчики приложений и сторонние организации. Стейкхолдеры данных владеют и контролируют доступ к данным, которые вам нужны, обычно это обрабатывается ИТ-отделом, группой управления данными или сторонними организациями. Системные и данные стейкхолдеры часто являются разными людьми или командами; иногда это одни и те же люди.
Рисунок 5-12. Вышестоящие стейкхолдеры инженера данных
Вы часто зависите от способности стейкхолдеров следовать правильным практикам инженерии программного обеспечения, управления базами данных и разработки. Идеально, если стейкхолдеры занимаются DevOps и работают в гибкой манере. Мы предлагаем создать обратную связь между инженерами данных и стейкхолдерами исходных систем, чтобы повысить осведомлённость о том, как данные потребляются и используются. Это одна из наиболее упускаемых из сфер, где инженеры данных могут получить большую ценность. Когда что-то происходит с исходными данными вверх по течению — а это обязательно произойдёт, будь то изменение схемы или данных, сбой сервера или базы данных, или другие важные события — вы хотите быть уверены, что осведомлены о влиянии этих проблем на ваши системы инженерии данных.

Может быть полезно иметь договор о данных с владельцами исходных систем вверх по течению. Что такое договор о данных? Джеймс Денмор предлагает следующее определение:
Договор о данных — это письменное соглашение между владельцем исходной системы и командой, поглощающей данные из этой системы для использования в конвейере данных. Договор должен указывать, какие данные извлекаются, каким методом (полным или инкрементальным), как часто, а также кто (лицо или команда) являются контактными лицами как для исходной системы, так и для поглощения данных. Договоры о данных должны храниться в известном и легкодоступном месте, таком как репозиторий GitHub или внутренний сайт документации. Если возможно, форматируйте договоры о данных в стандартизированной форме, чтобы их можно было интегрировать в процесс разработки или запрашивать программно.
Кроме того, рассмотрите возможность установления SLA (соглашения об уровне обслуживания) с поставщиками вверх по течению. SLA устанавливает ожидания относительно того, что вы можете ожидать от исходных систем, на которые полагаетесь. Примером SLA может быть: "данные из исходных систем будут надёжно доступны и высокого качества". SLO (цель уровня обслуживания) измеряет производительность по сравнению с тем, о чём вы договорились в SLA. Например, при вашем примере SLA, SLO может быть: "исходные системы будут иметь 99% времени безотказной работы". Если договор о данных или SLA/SLO кажется слишком формальным, по крайней мере, устно установите ожидания относительно гарантий исходных систем по времени безотказной работы, качеству данных и любым другим важным для вас аспектам. Владельцы исходных систем вверх по течению должны понимать ваши требования, чтобы они могли предоставить вам необходимые данные.

Фоновые процессы и их влияние на исходные системы

В отличие от других частей жизненного цикла инженерии данных, исходные системы обычно находятся вне контроля инженера данных. Существует неявное предположение (некоторые могут назвать это надеждой), что стейкхолдеры и владельцы исходных систем — и данных, которые они производят — следуют лучшим практикам в области управления данными, DataOps (и DevOps), DODD (упомянуто в Главе 2), архитектуры данных, оркестрации и инженерии программного обеспечения. Инженер данных должен получить как можно больше поддержки вверх по течению, чтобы убедиться, что подводные течения применяются, когда данные генерируются в исходных системах. Это сделает остальные этапы жизненного цикла инженерии данных более гладкими.

Как подводные течения влияют на исходные системы? Давайте посмотрим.

Безопасность

Безопасность критически важна, и последнее, чего вы хотите, — это случайно создать уязвимость в исходной системе. Вот несколько аспектов, которые следует учитывать:
■ Архитектура исходной системы обеспечивает безопасность и шифрование данных как в состоянии покоя, так и при передаче?
■ Нужно ли вам получать доступ к исходной системе через публичный интернет, или вы используете виртуальную частную сеть (VPN)?
■ Храните пароли, токены и учетные данные для доступа к исходной системе в безопасном месте. Например, если вы используете ключи Secure Shell (SSH), используйте менеджер ключей для защиты ваших ключей; то же правило применяется к паролям — используйте менеджер паролей или поставщика единого входа (SSO).
■ Доверяете ли вы исходной системе? Всегда убедитесь, что вы доверяете, но проверяете, что исходная система легитимна. Вы не хотите получать данные от злоумышленника.

Управление данными

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

Вот несколько аспектов, которые следует учитывать:

Управление данными
Управляются ли данные и системы вверх по течению надёжным и понятным образом? Кто управляет данными?

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

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

Управление мастер-данными
Контролируется ли создание записей вверх по течению практикой или системой управления мастер-данными?

Конфиденциальность и этика
Имеете ли вы доступ к сырым данным, или данные будут зашифрованы? Каковы последствия исходных данных? Как долго они сохраняются? Перемещаются ли они в зависимости от политик хранения?

Регулирование
На основании регуляций, должны ли вы иметь доступ к данным?

DataOps

Операционное совершенство — DevOps, DataOps, MLOps, XOps — должно распространяться на весь стек и поддерживать инженерию данных и их жизненный цикл. Хотя это идеально, часто это не полностью реализуется.

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

Установите чёткую цепочку коммуникаций между инженерией данных и командами, поддерживающими исходные системы. Идеально, если эти команды стейкхолдеров интегрировали DevOps в свой рабочий процесс и культуру. Это поможет значительно достичь целей DataOps (родственника DevOps), чтобы быстро устранять и уменьшать ошибки. Как мы упоминали ранее, инженеры данных должны внедрять себя в практики DevOps стейкхолдеров, и наоборот. Успешная DataOps работает, когда все участники настроены на то, чтобы системы работали целостно.

Несколько аспектов DataOps для рассмотрения:

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

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

Реагирование на инциденты
Каков ваш план, если что-то пойдёт не так? Например, как поведёт себя ваш конвейер данных, если исходная система выйдет из строя? Каков ваш план по восстановлению «потерянных» данных после того, как исходная система снова будет в сети?

Заголовок третьего уровня

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

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

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

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

Доступность
Какие гарантии того, что исходная система работает и доступна, когда это необходимо?

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

Оркестрация

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

Вот несколько аспектов, которые следует учитывать при оркестрации для исходных систем:

Ритм и частота
Доступны ли данные по фиксированному расписанию, или вы можете получить доступ к новым данным в любое время, когда захотите?

Общие фреймворки
Используют ли инженеры программного обеспечения и данных один и тот же менеджер контейнеров, такой как Kubernetes? Будет ли разумно интегрировать приложения и рабочие нагрузки данных в один и тот же кластер Kubernetes? Если вы используете фреймворк оркестрации, такой как Airflow, будет ли разумно интегрировать его с командой приложений вверх по течению? Здесь нет правильного ответа, но вам нужно балансировать преимущества интеграции с рисками тесной связи.

Программная инженерия

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

Сетевое взаимодействие
Убедитесь, что ваш код сможет получить доступ к сети, в которой находится исходная система. Также всегда думайте о безопасности сетевого взаимодействия. Вы получаете доступ к HTTPS URL через публичный интернет, SSH или VPN?

Аутентификация и авторизация
У вас есть соответствующие учетные данные (токены, имя пользователя/пароли) для доступа к исходной системе? Где вы будете хранить эти учетные данные, чтобы они не появлялись в вашем коде или системе контроля версий? У вас есть правильные роли IAM для выполнения закодированных задач?

Паттерны доступа
Как вы получаете доступ к данным? Используете ли вы API, и как вы обрабатываете запросы REST/GraphQL, объемы данных ответов и пагинацию? Если вы получаете доступ к данным через драйвер базы данных, совместим ли драйвер с базой данных, к которой вы получаете доступ? Для любого из паттернов доступа, как обрабатываются такие вещи, как повторные попытки и тайм-ауты?

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

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

Развёртывание
Как вы обрабатываете развёртывание изменений исходного кода?

Заключение

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

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

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

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

Дополнительные ресурсы

■ Schema Evolution and Compatibility
■ Database Internals, Alex Petrov (O'Reilly)
■ Database System Concepts, Abraham (Avi) Silberschatz et al. (McGraw Hill)
■ "The Log: What Every Software Engineer Should Know About Real-Time Data’s
Unifying Abstraction", Jay Kreps
■ "Modernizing Business Data Indexing", Benjamin Douglas and Mohammad
Mohtasham
■ "NoSQL: What’s in a Name", Eric Evans
■ "Test Data Quality at Scale with Deequ", Dustin Lange et al.
■ "The What, Why, and When of Single-Table Design with DynamoDB", Alex
DeBrie

■ Другие статьи на тему Базы данных

Показать еще