Глава 1. Компромиссы в архитектуре систем данных

Перевод из книги «Designing Data-Intensive Applications, 2nd Edition» подготовлен автором сайта

Table of Contents

Глава 1. Компромиссы в архитектуре систем обработки данных

Решений не существует — существуют лишь компромиссы. […] Но вы стараетесь добиться наилучшего возможного компромисса, и это всё, на что вы можете рассчитывать.

Томас Соул, интервью с Фредом Барнсом (2005)


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

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

Мы называем приложение «ориентированным на данные» (data-intensive), если управление данными является одной из ключевых задач при его разработке. В то время как в системах, ориентированных на вычисления, основной вызов заключается в параллелизации ресурсоёмких вычислений, в ориентированных на данные приложениях мы чаще беспокоимся о таких вещах, как хранение и обработка больших объёмов данных, управление изменениями данных, обеспечение согласованности при сбоях и параллельном доступе, а также достижение высокой доступности сервисов.

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

  • Хранить данные, чтобы они сами или другое приложение могли получить к ним доступ позже (базы данных)
  • Запоминать результат дорогостоящей операции, чтобы ускорить чтение (кэши)
  • Позволять пользователям искать данные по ключевым словам или фильтровать их различными способами (поисковые индексы)
  • Обрабатывать события и изменения данных по мере их возникновения (стриминг/обработка потоков)
  • Периодически обрабатывать большие накопленные объёмы данных (пакетная обработка)

При построении приложения мы обычно берём несколько программных систем или сервисов — таких как базы данных или API — и объединяем их с помощью собственного прикладного кода. Если вы используете системы данных строго в соответствии с тем, для чего они были предназначены, то процесс может быть довольно простым.

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

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

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

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

Чтобы помочь вам разобраться в возможных вариантах, эта глава сравнивает несколько противоположных концепций и рассматривает их компромиссы:

  • различие между операционными и аналитическими системами («Analytical versus Operational Systems»);
  • плюсы и минусы облачных сервисов и систем с самостоятельным размещением («Cloud versus Self-Hosting»);
  • когда следует переходить от одноузловых систем к распределённым системам («Distributed versus Single-Node Systems»);
  • и как сбалансировать потребности бизнеса и права пользователя («Data Systems, Law, and Society»).

Кроме того, эта глава предоставит вам терминологию, которая понадобится нам в остальной части книги.

ТЕРМИНОЛОГИЯ: ФРОНТЕНДЫ И БЭКЕНДЫ

Многое из того, что мы будем обсуждать в этой книге, относится к разработке бэкенда. Чтобы объяснить этот термин: в веб-приложениях клиентский код (который выполняется в веб-браузере) называется фронтендом, а серверный код, который обрабатывает пользовательские запросы, называется бэкендом. Мобильные приложения аналогичны фронтендам тем, что предоставляют пользовательские интерфейсы, которые часто взаимодействуют по Интернету с серверным бэкендом. Иногда фронтенды управляют данными локально на устройстве пользователя, но основные проблемы инфраструктуры данных чаще всего лежат на стороне бэкенда: фронтенду необходимо работать лишь с данными одного пользователя, тогда как бэкенд управляет данными от имени всех пользователей.

Бэкенд-сервис обычно доступен по HTTP (иногда через WebSocket); он обычно состоит из некоторого прикладного кода, который читает и записывает данные в одну или несколько баз данных, а также иногда взаимодействует с дополнительными системами данных, такими как кэши или очереди сообщений (всё это вместе мы можем называть инфраструктурой данных). Прикладной код часто является «без состояния» (т.е. после завершения обработки одного HTTP-запроса он забывает всё, что касается этого запроса), и любая информация, которую необходимо сохранить между запросами, должна храниться либо на клиенте, либо в серверной инфраструктуре данных.

Аналитические против операционных систем (Analytical versus Operational Systems)

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

В дополнение к командам, управляющим бэкенд-сервисами, обычно есть две другие группы людей, которым требуется доступ к данным организации: бизнес-аналитики, которые формируют отчёты о деятельности организации, чтобы помочь руководству принимать более обоснованные решения (business intelligence, или BI), и дата-сайентисты, которые ищут новые инсайты в данных или создают пользовательские функции на основе анализа данных и машинного обучения/ИИ (например, рекомендации на сайте электронной коммерции «покупатели, купившие X, также купили Y», предиктивная аналитика вроде скоринга рисков или фильтрации спама, а также ранжирование результатов поиска).

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

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

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

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

Характеристика обработки транзакций и аналитики (Characterizing Transaction Processing and Analytics)

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

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

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

  1. Какова была общая выручка каждого из наших магазинов в январе?
  2. Насколько больше обычного мы продали бананов во время последней акции?
  3. Какой бренд детского питания чаще всего покупают вместе с подгузниками бренда X?

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

Таблица 1-1. Сравнение характеристик операционных и аналитических систем

Свойство Операционные системы (OLTP) Аналитические системы (OLAP)
Основной шаблон чтения Точечные запросы (получение отдельных записей по ключу) Агрегация по большому количеству записей
Основной шаблон записи Создание, обновление и удаление отдельных записей Массовый импорт (ETL) или поток событий
Пример пользователя-человека Конечный пользователь веб-/мобильного приложения Внутренний аналитик для поддержки принятия решений
Пример машинного использования Проверка, разрешено ли действие Обнаружение шаблонов мошенничества/злоупотреблений
Тип запросов Фиксированный набор запросов, предопределённых в приложении Аналитик может выполнять произвольные запросы
Представляемые данные Актуальное состояние данных (текущий момент времени) История событий, произошедших с течением времени
Размер датасета От гигабайт до терабайт От терабайт до петабайт

ПРИМЕЧАНИЕ
Значение слова онлайн в OLAP не вполне ясно; вероятно, оно указывает на то, что запросы выполняются не только для предопределённых отчётов, но и интерактивно — аналитики используют систему OLAP для исследовательских запросов.


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

Существует также тип систем, предназначенных для аналитических нагрузок (запросов, агрегирующих множество записей), но встроенных в пользовательские продукты. Эта категория известна как продуктовая аналитика или аналитика в реальном времени, и к системам, спроектированным для такого использования, относятся Pinot, Druid и ClickHouse.

Хранилище данных (Data Warehousing)

Поначалу одни и те же базы данных использовались как для обработки транзакций, так и для аналитических запросов. SQL оказался довольно гибким в этом отношении: он хорошо подходит для обоих типов запросов. Тем не менее, в конце 1980-х и начале 1990-х годов появилась тенденция: компании стали отказываться от использования своих OLTP-систем для аналитики и начали выполнять аналитику на отдельной системе баз данных. Эта отдельная база данных получила название хранилище данных (data warehouse).

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

Обычно нецелесообразно, чтобы бизнес-аналитики и дата-сайентисты напрямую делали запросы к этим OLTP-системам, по нескольким причинам:

  • интересующие данные могут быть распределены между несколькими операционными системами, и их сложно объединить в одном запросе (эта проблема известна как информационные силосы, data silos);
  • схемы и структуры данных, оптимальные для OLTP, плохо подходят для аналитики (см. «Звёзды и снежинки: схемы для аналитики»);
  • аналитические запросы могут быть весьма ресурсоёмкими, и их выполнение на базе данных OLTP может повлиять на производительность для других пользователей;
  • системы OLTP могут находиться в отдельной сети, к которой у пользователей нет прямого доступа по соображениям безопасности или соответствия нормативам.

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

Хранилище данных содержит только для чтения копию данных из всех различных OLTP-систем компании. Данные извлекаются из OLTP-баз данных (с помощью периодического дампа данных или непрерывного потока обновлений), трансформируются в схему, удобную для анализа, очищаются, а затем загружаются в хранилище данных. Этот процесс загрузки данных в хранилище известен как Extract–Transform–Load (ETL) и показан на Рисунке 1-1. Иногда порядок шагов transform и load меняется местами (т.е. трансформация выполняется в хранилище данных, после загрузки), что приводит к схеме ELT.

Рисунок 1-1. Упрощённая схема ETL в хранилище данных

В некоторых случаях источниками данных для ETL-процессов являются внешние SaaS-продукты, такие как системы управления взаимоотношениями с клиентами (CRM), email-маркетинг или системы обработки кредитных карт. В таких случаях у вас нет прямого доступа к исходной базе данных, поскольку она доступна только через API поставщика программного обеспечения. Перенос данных из этих внешних систем в собственное хранилище данных может дать возможность выполнять анализы, которые невозможны через SaaS API. ETL для SaaS API часто реализуется с помощью специализированных сервисов-коннекторов данных, таких как Fivetran, Singer или AirByte.

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

Более того, несмотря на существование HTAP, разделение транзакционных и аналитических систем является обычной практикой из-за различий в целях и требованиях. В частности, считается хорошей практикой, чтобы каждая операционная система имела свою собственную базу данных (см. «Микросервисы и Serverless»), что приводит к сотням отдельных операционных баз данных; в то время как у предприятия, как правило, имеется одно хранилище данных, чтобы бизнес-аналитики могли объединять данные из нескольких операционных систем в одном запросе.

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

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

От хранилища данных к озеру данных (Data Lake)

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

Преобразование данных в форму, пригодную для обучения модели машинного обучения; часто это требует преобразования строк и столбцов таблицы базы данных в вектор или матрицу числовых значений, называемых признаками. Процесс выполнения такого преобразования с целью максимизации производительности обученной модели называется разработкой признаков (feature engineering), и он часто требует пользовательского кода, который трудно выразить с помощью SQL.

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

Хотя предпринимались попытки добавить операторы машинного обучения в SQL-модель данных и построить эффективные системы машинного обучения на реляционной основе, многие дата-сайентисты предпочитают не работать в реляционных базах данных, таких как хранилище данных. Вместо этого они часто предпочитают использовать библиотеки анализа данных на Python, такие как pandas и scikit-learn, языки статистического анализа, такие как R, и распределённые фреймворки аналитики, такие как Spark. Мы обсудим это подробнее в разделе «Dataframes, Matrices, and Arrays».

В результате организации сталкиваются с необходимостью предоставлять данные в форме, пригодной для использования дата-сайентистами. Ответом на эту потребность стало озеро данных: централизованное хранилище, в котором содержится копия любых данных, которые могут быть полезны для анализа, полученных из операционных систем через ETL-процессы. Различие между хранилищем данных и озером данных заключается в том, что озеро данных просто содержит файлы, не накладывая никаких требований к формату файлов или модели данных. Файлы в озере данных могут быть коллекциями записей базы данных, закодированными в формате файлов, таком как Avro или Parquet (см. Главу 5), но они также могут содержать текст, изображения, видео, данные сенсоров, разреженные матрицы, векторы признаков, последовательности генома или любые другие типы данных. Помимо большей гибкости, это также зачастую дешевле, чем реляционное хранение данных, поскольку озеро данных может использовать стандартизированное файловое хранилище, такое как object storage (см. «Архитектура облачно-нативных систем»).

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

Помимо загрузки данных из озера данных в отдельное хранилище данных, также возможно выполнение типичных рабочих нагрузок хранилища данных (SQL-запросы и бизнес-аналитика) напрямую по файлам в озере данных, параллельно с задачами в области data science и машинного обучения. Такая архитектура называется data lakehouse и требует наличия движка выполнения запросов и слоя метаданных (например, управления схемами), расширяющих файловое хранилище озера данных. Примеры такого подхода включают Apache Hive, Spark SQL, Presto и Trino.

За пределами озера данных

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

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

В некоторых случаях результаты работы аналитических систем передаются в операционные системы (процесс, иногда называемый обратным ETL). Например, модель машинного обучения, обученная на данных в аналитической системе, может быть развернута в продакшене, чтобы формировать рекомендации для конечных пользователей, такие как «покупатели X также покупали Y». Такие развернутые результаты аналитических систем также называют продуктами данных. Модели машинного обучения могут быть развернуты в операционных системах с помощью специализированных инструментов, таких как TFX, Kubeflow или MLflow.

Системы записи и производные данные (Systems of Record and Derived Data)

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

Системы записи

Система записи, также известная как источник истины, содержит авторитетную или каноническую версию каких-либо данных. Когда поступают новые данные, например, от пользователя, они сначала записываются сюда. Каждый факт представлен ровно один раз (представление, как правило, нормализовано; см. «Нормализация, денормализация и объединения»). Если существует несоответствие между другой системой и системой записи, то значение в системе записи считается (по определению) правильным.

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

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

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

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

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

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

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

Облако против самостоятельного размещения (Cloud versus Self-Hosting)

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

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

В программном обеспечении необходимо принять два важных решения: кто будет разрабатывать программное обеспечение и кто будет его развёртывать. Существует спектр вариантов, где каждое из этих решений может быть передано на аутсорсинг в разной степени, как показано на рисунке 1-2. На одном конце спектра — индивидуальное программное обеспечение, которое вы пишете и запускаете внутри компании; на другом — широко используемые облачные сервисы или SaaS-продукты, которые разрабатываются и эксплуатируются внешним поставщиком, а вы получаете к ним доступ только через веб-интерфейс или API.

Рисунок 1-2. Спектр типов программного обеспечения и его эксплуатации

Промежуточный вариант — это готовое программное обеспечение (open source или коммерческое), которое вы размещаете самостоятельно, то есть разворачиваете собственными силами — например, если вы скачиваете MySQL и устанавливаете его на сервер, находящийся под вашим контролем. Это может быть как на вашем собственном оборудовании (часто называемом on-premises, даже если сервер физически находится в арендованной стойке в дата-центре, а не буквально у вас на территории), так и на виртуальной машине в облаке (инфраструктура как услуга, или IaaS). На этом спектре есть и другие точки, например, запуск модифицированной версии open source ПО.

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

Плюсы и минусы облачных сервисов

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

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

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

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

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

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

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

  • Если не хватает нужной вам функции, всё, что вы можете сделать — это вежливо попросить поставщика её добавить; как правило, вы не можете реализовать её сами.
  • Если сервис выходит из строя, вы можете только ждать его восстановления.
  • Если вы используете сервис таким образом, что это вызывает баг или проблемы с производительностью, вам будет сложно диагностировать проблему. В случае программного обеспечения, которое вы запускаете самостоятельно, вы можете получить метрики производительности и отладочную информацию от операционной системы, чтобы понять поведение, и можете просматривать журналы сервера, но при использовании сервиса у поставщика вы обычно не имеете доступа к этим внутренностям.
  • Кроме того, если сервис прекращает работу или становится неприемлемо дорогим, или если поставщик решает изменить продукт так, как вам не нравится, вы в его власти — продолжать использовать старую версию ПО обычно нельзя, и вам придётся переходить на альтернативный сервис. Этот риск уменьшается, если существуют альтернативные сервисы с совместимым API, но для многих облачных сервисов стандартные API отсутствуют, что повышает стоимость перехода и делает проблему привязки к поставщику (vendor lock-in) актуальной.
  • Облачному провайдеру необходимо доверять хранение данных в безопасности, что может усложнить процесс соблюдения требований по конфиденциальности и безопасности.

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

Архитектура облачно-ориентированных систем

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

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

Таблица 1-2. Примеры самостоятельно развёртываемых и облачно-ориентированных СУБД

Категория Самостоятельное размещение Облачно-ориентированные системы
Операционные / OLTP MySQL, PostgreSQL, MongoDB AWS Aurora [25], Azure SQL DB Hyperscale [26], Google Cloud Spanner
Аналитические / OLAP Teradata, ClickHouse, Spark Snowflake [27], Google BigQuery, Azure Synapse Analytics

Слоистость облачных сервисов (Layering of cloud services)

У многих самостоятельно развёртываемых информационных систем очень простые системные требования: они работают на обычной операционной системе, такой как Linux или Windows, хранят свои данные в виде файлов в файловой системе и обмениваются данными через стандартные сетевые протоколы, такие как TCP/IP. Некоторые системы зависят от специализированного оборудования, такого как графические процессоры (для машинного обучения) или сетевые интерфейсы с RDMA, но в целом самостоятельно развёртываемое программное обеспечение, как правило, использует очень универсальные вычислительные ресурсы: CPU, RAM, файловую систему и IP-сеть.

В облаке такой тип программного обеспечения может работать в среде Infrastructure-as-a-Service, используя одну или несколько виртуальных машин (или инстансов) с определённым объёмом CPU, памяти, диска и пропускной способности сети. По сравнению с физическими машинами, облачные инстансы можно быстрее задействовать, и они доступны в большем разнообразии размеров, но в остальном они схожи с традиционным компьютером: вы можете запускать на нём любое программное обеспечение, но сами отвечаете за его администрирование.

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

Сервисы объектного хранения, такие как Amazon S3, Azure Blob Storage и Cloudflare R2, хранят большие файлы. Они предоставляют более ограниченные API по сравнению с типичной файловой системой (основные операции чтения и записи файлов), но имеют то преимущество, что скрывают под собой физические машины: сервис автоматически распределяет данные по множеству машин, так что вам не нужно беспокоиться о том, что на какой-то машине закончится место на диске. Даже если какие-то машины или их диски полностью выйдут из строя, данные не будут потеряны.

Многие другие сервисы, в свою очередь, построены на объектном хранилище и других облачных сервисах: например, Snowflake — это облачная аналитическая база данных (хранилище данных), которая использует S3 для хранения данных, а некоторые другие сервисы, в свою очередь, строятся на базе Snowflake.

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

Разделение хранилища и вычислений (storage and compute)

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

В качестве альтернативы локальным дискам облачные сервисы также предлагают виртуальное дисковое хранилище, которое можно отсоединить от одного инстанса и подключить к другому (Amazon EBS, управляемые диски в Azure и постоянные диски в Google Cloud). Такой виртуальный диск на самом деле не является физическим диском, а представляет собой облачный сервис, предоставляемый отдельным набором машин, который эмулирует поведение диска (блочного устройства, где каждый блок обычно имеет размер 4 КиБ). Эта технология позволяет запускать традиционное программное обеспечение, ориентированное на диски, в облаке, но эмуляция блочного устройства создаёт накладные расходы, которых можно избежать в системах, спроектированных с нуля для облака. Она также делает приложение очень чувствительным к сетевым сбоям, поскольку каждый ввод-вывод на виртуальном блочном устройстве на самом деле является сетевым вызовом.

Чтобы решить эту проблему, облачно-ориентированные сервисы, как правило, избегают использования виртуальных дисков и вместо этого строятся на специализированных хранилищах, оптимизированных под конкретные типы нагрузки. Сервисы объектного хранения, такие как S3, предназначены для долговременного хранения довольно больших файлов — от сотен килобайт до нескольких гигабайт. Отдельные строки или значения, хранящиеся в базе данных, как правило, намного меньше; поэтому облачные базы данных обычно управляют меньшими значениями в отдельном сервисе, а более крупные блоки данных (содержащие множество отдельных значений) хранят в объектном хранилище. Мы увидим способы реализации этого в главе 4.

В традиционной архитектуре систем один и тот же компьютер отвечает как за хранение (диск), так и за вычисления (CPU и RAM), но в облачно-ориентированных системах эти две функции в некоторой степени разделены или дезагрегированы: например, S3 просто хранит файлы, и если вы хотите проанализировать эти данные, вам нужно будет запускать код анализа где-то вне S3. Это подразумевает передачу данных по сети, о чём мы поговорим далее в разделе “Распределённые и одновузловые системы”.

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

Операции в эпоху облаков

Традиционно люди, управлявшие серверной частью инфраструктуры данных организации, назывались администраторами баз данных (DBA) или системными администраторами (системные админы). В последнее время многие организации стремятся интегрировать роли разработки программного обеспечения и эксплуатации в команды, разделяющие ответственность как за серверные сервисы, так и за инфраструктуру данных; этой тенденцией руководствуется философия DevOps. Инженеры надёжности сайтов (Site Reliability Engineers, SRE) — это реализация этой идеи в Google.

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

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

Этот сдвиг акцента с отдельных машин на сервисы сопровождается изменением роли операций. Высокоуровневая цель — обеспечение надёжной работы сервиса — остаётся той же, но процессы и инструменты эволюционируют. Философия DevOps/SRE делает больший упор на:

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

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

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

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

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

Распределённые и одно-узловые системы (Distributed versus Single-Node Systems)

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

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

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

Устойчивость к сбоям / высокая доступность
Если ваше приложение должно продолжать работать даже при отказе одной (или нескольких) машин, сети или даже целого дата-центра, можно использовать несколько машин для обеспечения избыточности. Когда одна выходит из строя, другая может её заменить. См. «Надёжность и устойчивость к сбоям» и главу 6 о репликации.

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

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

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

Использование специализированного оборудования
Разные части системы могут использовать различные типы оборудования в зависимости от нагрузки. Например, объектное хранилище может использовать машины с большим количеством дисков, но с малым числом процессоров, а система анализа данных — машины с большим количеством CPU и памяти, но без дисков, а система машинного обучения — машины с GPU (которые намного эффективнее CPU при обучении нейронных сетей и других задачах машинного обучения).

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

Эти причины применимы как к сервисам, которые вы разрабатываете сами (прикладной код), так и к сервисам, основанным на готовом программном обеспечении (например, базы данных).

Проблемы распределённых систем

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

Хотя сети в дата-центрах быстрые, вызов другого сервиса всё равно значительно медленнее, чем вызов функции в одном процессе. При работе с большими объёмами данных вместо передачи данных с хранилища на отдельную машину для обработки может быть быстрее перенести вычисление на ту машину, где данные уже находятся. Большее количество узлов — не всегда быстрее: в некоторых случаях простая однопоточная программа на одном компьютере может работать значительно лучше, чем кластер с более чем 100 ядрами CPU.

Отладка распределённой системы часто бывает затруднена: если система отвечает медленно, как понять, где именно возникла проблема? Методы диагностики проблем в распределённых системах развиваются в рамках подхода под названием наблюдаемость (observability), который включает сбор данных о выполнении системы и возможность их анализа как на уровне высокоуровневых метрик, так и отдельных событий. Инструменты трассировки, такие как OpenTelemetry, Zipkin и Jaeger, позволяют отслеживать, какой клиент вызвал какой сервер и для какой операции, а также сколько времени занял каждый вызов.

Базы данных предоставляют различные механизмы обеспечения согласованности данных, как мы увидим в главах 6 и 8. Однако когда каждый сервис имеет свою собственную базу данных, поддержание согласованности данных между этими разными сервисами становится задачей приложения. Распределённые транзакции — это возможный способ обеспечения согласованности, но они редко применяются в контексте микросервисов, так как противоречат цели независимости сервисов, и многие базы данных их не поддерживают.

По всем этим причинам, если задачу можно выполнить на одной машине, это часто будет проще и дешевле, чем настройка распределённой системы. CPU, память и диски стали больше, быстрее и надёжнее. В сочетании с одноузловыми базами данных, такими как DuckDB, SQLite и KùzuDB, многие нагрузки теперь могут выполняться на одном узле. Мы подробнее рассмотрим эту тему в главе 4.

Микросервисы и Serverless

Наиболее распространённый способ распределения системы между несколькими машинами — это разделение на клиенты и серверы, где клиенты отправляют запросы серверам. Обычно для такой коммуникации используется HTTP, как мы обсудим в разделе «Поток данных через сервисы: REST и RPC». Один и тот же процесс может быть как сервером (обрабатывая входящие запросы), так и клиентом (отправляя запросы другим сервисам).

Такой подход к построению приложений традиционно назывался сервис-ориентированной архитектурой (SOA); в более новой трактовке это архитектура микросервисов. В такой архитектуре сервис выполняет одну чётко определённую функцию (например, в случае S3 — это хранение файлов); каждый сервис предоставляет API, доступный по сети, и за каждый сервис отвечает одна команда. Сложное приложение можно таким образом разложить на множество взаимодействующих сервисов, каждый из которых управляется своей отдельной командой.

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

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

API микросервисов бывает сложно развивать. Клиенты, вызывающие API, ожидают определённые поля. Разработчики могут захотеть добавить или удалить поля в API по мере изменения бизнес-требований, но это может привести к сбоям у клиентов. Хуже всего, что такие сбои часто обнаруживаются только на поздних стадиях разработки, когда обновлённый API развёртывается в staging или production. Стандарты описания API, такие как OpenAPI и gRPC, помогают управлять отношениями между клиентами и серверами; мы рассмотрим их подробнее в главе 5.

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

Serverless, или функция как сервис (FaaS) — это другой подход к развёртыванию сервисов, при котором управление инфраструктурой передаётся облачному провайдеру. При использовании виртуальных машин вы сами определяете, когда запустить или остановить экземпляр; в serverless-модели провайдер автоматически выделяет и освобождает ресурсы по мере поступления запросов к вашему сервису. Такой подход снимает часть операционной нагрузки с пользователя и предлагает гибкую модель оплаты по факту использования, а не за зарезервированные машины. Чтобы обеспечить такие преимущества, провайдеры serverless-инфраструктур часто накладывают ограничения на длительность выполнения функций, доступные среды исполнения и могут страдать от «холодного старта», когда функция вызывается впервые. Термин serverless может быть немного вводящим в заблуждение: каждая функция всё равно запускается на сервере, просто разные вызовы могут быть выполнены на разных серверах. Более того, инфраструктуры вроде BigQuery и различные предложения Kafka тоже используют термин serverless, чтобы подчеркнуть автомасштабирование и оплату за фактическое использование, а не за выделенные серверы.

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

Облачные вычисления против суперкомпьютеров

Облачные вычисления — не единственный способ построения крупномасштабных вычислительных систем; альтернативой являются вычисления высокой производительности (HPC, High-Performance Computing), также известные как суперкомпьютеры. Хотя между ними есть пересечения, HPC часто имеет другие приоритеты и использует иные подходы по сравнению с облачными вычислениями и корпоративными дата-центрами. Некоторые из этих различий:

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

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

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

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

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

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

Системы работы с данными, закон и общество (Data Systems, Law, and Society)

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

Особую озабоченность вызывают системы, хранящие данные о людях и их поведении. С 2018 года Общий регламент по защите данных (GDPR) дал жителям многих европейских стран больше контроля и юридических прав в отношении их персональных данных, и подобные законы о конфиденциальности были приняты в ряде других стран и штатов, включая, например, Закон о конфиденциальности потребителей Калифорнии (CCPA). Регулирование в области ИИ, такое как Закон об ИИ ЕС, вводит дополнительные ограничения на использование персональных данных.

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

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

Юридические аспекты всё больше влияют на самые основы проектирования систем работы с данными. Например, GDPR предоставляет людям право требовать удаления своих данных по запросу (так называемое «право быть забытым»). Однако, как мы увидим в этой книге, многие системы работы с данными полагаются на неизменяемые конструкции, такие как журналы только для добавления. Как же тогда можно удалить данные в середине файла, который, по задумке, должен быть неизменяемым? Как мы можем удалить данные, которые были включены в производные наборы данных (см. «Системы записи и производные данные»), например, в обучающие данные для моделей машинного обучения? Ответы на эти вопросы создают новые инженерные задачи.

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

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

Государственные органы или полиция также могут потребовать от компаний передать данные. Если есть риск, что данные могут указать на криминализированное поведение (например, гомосексуальность в ряде стран Ближнего Востока и Африки или обращение за абортом в нескольких штатах США), хранение таких данных создаёт реальные угрозы безопасности для пользователей. Поездка в клинику, делающую аборты, может быть легко раскрыта через данные о местоположении, возможно, даже через журнал IP-адресов пользователя (указывающий примерное местоположение во времени).

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

Бизнес также начал учитывать вопросы конфиденциальности и безопасности. Компании, обрабатывающие платежи, обязаны соблюдать строгие стандарты индустрии платёжных карт (PCI). Они проходят частые проверки сторонними аудиторами для подтверждения соответствия. Поставщики ПО также подвергаются всё большей проверке. Многие заказчики теперь требуют, чтобы поставщики соответствовали стандартам SOC (Service Organization Control) типа 2. Как и с PCI, поставщики проходят аудит третьих сторон для подтверждения соответствия.

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

Краткая сводка по 1 главе

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

Начали с различия между операционными (OLTP) и аналитическими (OLAP) системами, их особенностями, разными типами данных и разными пользователями. Познакомились с концепциями хранилища данных (data warehouse) и озера данных (data lake), которые получают данные из операционных систем через ETL. В следующей главе будет показано, что внутреннее устройство данных в этих системах сильно различается из-за разных типов запросов.

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

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

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

0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x