Агрегированные модели и NoSQL базы данных

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



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

В последние несколько десятилетий доминирующей была реляционная модель дан­ных, которая лучше всего представляется в виде набора таблиц, напоминающих стра­ницы электронных таблиц. Каждая таблица состоит из строк, представляющих какую- то сущность. Мы описываем эту сущность с помощью столбцов, каждый из которых имеет отдельное значение. Столбец может относиться к другой строке той же или дру­гой таблицы. Таким образом, возникают связи между сущностями. (Говоря о таблицах и строках, мы используем неформальную, но распространенную терминологию; более формальными терминами являются “отношения” и “кортежи”.)

Одной из основных особенностей технологии NoSQL является отказ от реляцион­ной модели. Каждое решение в рамках технологии NoSQL использует свою собственную модель. Эти модели разделяются на четыре категории: ключ—значение, документ, семейство столбцов и граф. Первые три модели имеют общее свойство, которое мы назовем агрегатной ориентацией (aggregate orientation). В этой главе объясняется, что мы понимаем под агрегатной ориентацией и что это значит для моделей данных.

 

Агрегаты

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

Агрегатная ориентация придерживается другого подхода. Она учитывает необхо­димость оперировать данными, имеющими более сложную структуру, чем набор кор­тежей. Ее можно описать в терминах сложной записи, которая может содержать спи­ски и другие структуры записей. Как мы увидим, базы данных типа “ключ-значение”, документ и семейство столбцов могут содержать сложные записи. Однако для этой сложной записи нет общепринятого термина; в книге мы называем ее агрегатом (aggregate).

Агрегат — это термин, пришедший из предметно-ориентированного проекти­рования [Evans]. В предметно-ориентированном проектировании агрегатом назы­вают коллекцию связанных объектов, которая интерпретируется как единое целое. В частности, она представляет собой единицу для манипулирования данными и управления их согласованностью. Обычно мы модифицируем агрегаты с помощью атомарных операций и взаимодействуем с хранилищем данных посредством агрега­тов. Это определение довольно точно описывает принципы работы баз данных типа “ключ-значение”, документ и семейство значений. Агрегаты облегчают работу баз данных на кластерах, поскольку агрегат представляет собой естественную единицу репликации и фрагментации. Кроме того, агрегаты упрощают разработку приклад­ных программ, которые часто манипулируют данными с помощью агрегированных структур.

 

Пример отношений и агрегатов

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

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

Moдель данных, ориентированная на реляционную базу банных

 Рис. 1. Модель данных, ориентированная на реляционную базу данных (в обо­значениях UML [Fowler UNL])

На рис. 2 представлены некие простые данные для этой модели.

Типичные данные для использования в модели RDBMS

Рис. 2. Типичные данные для использования в модели RDBMS

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

Агрегатная модель данных

Рис. 3. Агрегатная модель данных

Итак, у нас есть простые данные, которые мы представим в формате JSON, кото­рый является основным способом представления данных в технологии NoSQL.

// Клиенты
{
"id":l,
"name":"Martin",
"ЬillingAddress": [ { "ci ty": "Chicago"}]
}
// Заказы
{
"id":99,
"customer!d":l,
"orderitems":[
   {
   "productid":27,
   "price": 32. 45,
   "productName": "NoSQL Distilled"
   }
] ,
"shippingAddress": [ { "city": "Chicago"}]
"orderPayment": [
   {
   "ccinfo":"l000-1000-1000-1000",
   "txnid":"abelif879rft",
   "billingAddress": {"city": "Chicago"}
   }
],
}

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

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

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

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

Объединение всех объектов, соответствующих клиенту и его заказам

Рис. 4. Объединение всех объектов, соответствующих клиенту и его заказам


Используя описанную выше модель данных, записи Customer и Order можно переписать следующим образом:

// Клиенты {
"customer": {
"id": 1,
"name": "Martin",
"billingAddress":	[{"city": "Chicago"}],
"orders":	[
   {
      "id":99,
      "customerld":1,
      "orderIterns":[
      {
         "productld":27,
         "price": 32.45,
         "productName": "NoSQL Distilled"
      }
   ],
   "shippingAddress":[{"city":"Chicago"}]
      "orderPayment":[
      {
         "ccinfo":"1000-1000-1000-1000",
         "txnId":"abelif879rft",
         "billingAddress" : { "city" : "Chicago" }
      }],
   }]
  }
}

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

 

Последствия ориентации на агрегаты

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

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

Реляционные базы данных не имеют концепции агрегата в своей модели данных, поэтому мы называем их безагрегатными (aggregate-ignorant). В технологии NoSQL гра­фовые базы данных также являются безагрегатными. Это свойство нельзя назвать недо­статком. Часто трудно правильно изобразить границы агрегатов, особенно если одни и те же данные используются в разных контекстах. Заказ представляет собой удобный агрегат, если клиент создает и просматривает заказы, а розничный продавец обрабаты­вает их. Однако, если продавец захочет проанализировать свои продажи за последние несколько месяцев, агрегат заказов станет для него проблемой. Для того чтобы по­лучить историю продаж, вы должны заглянуть в каждый агрегат в вашей базе данных. Итак, агрегатная структура может упростить одни операции с данными и усложнить другие. Безагрегатная модель позволяет легко просматривать данные разными спосо­бами, поэтому она является лучшим выбором, если у вас нет основной структуры для манипулирования данными.

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

Агрегаты оказывают сильное влияние на транзакции. Реляционные базы данных по­зволяют манипулировать любой комбинацией строк из любой таблицы в рамках одной транзакции. Такие транзакции называются ACID: атомарные (atomic), согласованные (consistent), изолированные (isolated) и долговечные (durable). ACID — это надуманная аббревиатура; главным пунктом является атомарность: многие строки из разных та­блиц обновляются в рамках одной операции. Эта операция завершается либо полным успехом, либо полной неудачей, причем параллельные операции изолируются друг от друга так, что они не могут выполнять частичные модификации.

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

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

 

Модель данных “ключ-значение” и документная модель

Ранее мы говорили о том, что базы данных типа “ключ—значение” и документные базы данных являются сильно агрегатно-ориентированными. Мы имели в виду, что эти базы данных в основном были сконструированы из агрегатов. Базы данных обоих типов состоят из множества агрегатов, каждый из которых имеет ключ или идентифи­катор, который используется для доступа к данным.

Эти две модели отличаются друг от друга тем, что в базе данных “ключ- значение” агрегат является непроницаемым для базы данных — просто большой черный ящик, состоящий из преимущественно бессмысленных битов. В противопо­ложность этому документная база может видеть структуру агрегата. Преимущество непрозрачности заключается в том, что в агрегате можно хранить все что угодно. База данных может ограничивать общий размер агрегата, но в остальном мы име­ем полную свободу. Документная база данных накладывает ограничения на то, что можно хранить в агрегате, определяя допустимые структуры и типы. Однако за это мы получаем большую гибкость доступа. В хранилище типа “ключ-значение” мы можем просматривать агрегат только с помощью его ключа. В документной базе данных мы можем посылать базе данных запросы, касающиеся полей в агрегате, извлекать части агрегата, а не весь агрегат целиком, причем база данных может соз­давать индексы с учетом содержимого агрегата. На практике разделительная линия между базами данных типа “ключ-значение” и документными базами данных до­вольна расплывчата. Люди часто записывают идентификаторы в документные базы данных, чтобы выполнять поиск в стиле “ключ-значение”. Базы данных, класси­фицированные как базы типа “ключ-значение”, могут предлагать новые структуры для данных, помимо непрозрачных агрегатов. Например, база данных Riak позво­ляет добавлять метаданные к агрегатам для индексирования и установления связей между агрегатами, a Redis позволяет разбивать агрегаты на списки или множества. Кроме того, можно обеспечить механизм запросов с помощью интегрированных средств поиска, как в базе данных Solr. Например, поисковый механизм базы данных Riak, аналогичный поисковому механизму базы Solr, выполняет поиск агрегатов, хранящихся в виде структур JSON или XML.

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

 

Хранилища типа “семейство столбцов”

Одной из ранних и популярных баз данных NoSQL была база BigTable компании Google [Chang etc.]. Ее имя вызывает в воображении табличную структуру, состоящую из отдельных столбцов и не имеющую схемы. Как вскоре будет показано, эту структуру не следует представлять в виде таблицы; скорее она представляет собой двухуровневый ассоциативный массив. Однако, как бы вы ни представляли себе эту структуру, эта мо­дель оказала влияние на более поздние базы данных, такие как HBase и Cassandra.

Эти базы данных с таблицами в стиле BigTable часто называют хранилищами столбцов, но это имя относится совсем к другой сущности. Хранилища столбцов, существовавшие до появления технологии NoSQL, такие как С-Store [C-Store], пре­красно уживались с языком SQL и реляционной моделью. Они отличались лишь спо­собом физического хранения данных. Большинство баз данных в качестве единицы хранения используют строки. Помимо всего прочего, это позволяет обеспечить вы­сокую производительность записи. Однако существует много сценариев, в которых записи выполняются редко, но приходится часто считывать по несколько столбцов из многих строк одновременно. В этой ситуации лучше считать единицей хранения группы столбцов для всех строк. Именно поэтому такие базы данных называются хра­нилищами столбцов.

База данных BigTable и ее потомки основаны на концепции хранения групп столб­цов (семейств столбцов), но, в отличие базы C-Store и ее аналогов, в них не исполь­зуются реляционная модель и язык SQL. В моем блоге мы называем этот класс баз данных семействами столбцов.

Вероятно, лучше всего представлять модель семейства столбцов как двухуровне­вую агрегатную структуру. Как и в хранилищах типа “ключ-значение”, главный ключ часто описывается как идентификатор строки, отмечая интересующий нас агрегат. От­личительной особенностью структур типа “семейство столбцов” является то, что эта строка-агрегат сама состоит из ассоциативного массива более детализированных значе­ний. Эти значения второго уровня называются столбцами. Помимо доступа к строкам как к единому целому, операции также допускают извлечение конкретного столбца, так что, для того чтобы получить имя клиента на рис. 5, мы могли бы написать команду наподобие get ('1234', 'name').

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

Представление информации о пользователе в виде структуры “семейство столбцов”

Рис. 5. Представление информации о пользователе в виде структуры “семейство столбцов”


Это открывает несколько возможностей для представления о том, как структури­рованы данные в базе.

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

Все сказанное относится к базам данных Google BigTable и HBase, но Cassandra не­много отличается от них. Строка в базе данных Cassandra возникает только в семействе столбцов, но это семейство может содержать суперстолбцы — столбцы, содержащие вложенные столбцы. Суперстолбцы в базе Cassandra являются ближайшим аналогом классических семейств столбцов BigTable.

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

Пример, приведенный на рис. 5, иллюстрирует другой аспект баз данных типа “семейство столбцов”, который может оказаться неизвестным людям, использующим реляционные базы данных: семейство столбцов orders. Поскольку столбцы можно добавлять свободно, список элементов можно легко моделировать, сделав каждый эле­мент отдельным столбцом. Если представлять себе семейство столбцов как таблицу, это может показаться очень странным, но если строку семейства столбцов интерпрети­ровать как агрегат, все становится на свои места. В базе данных Cassandra строки быва­ют широкими и “худыми”. “Худые” строки (skinny rows) содержат несколько столбцов, причем одни и те же столбцы используются в разных строках. В данном случае семей­ство столбцов определяет тип записи, каждая строка является записью, а каждый стол­бец — полем. Широкая строка (wide row) содержит много разных столбцов (возможно, тысячи). Широкое семейство столбцов моделирует список, в котором каждый столбец представляет собой элемент в этом списке.

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

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

 

Заключительные замечания об агрегатно­ориентированных базах данных

Мы рассмотрели достаточно много материала, чтобы сделать краткий обзор трех разных стилей агрегатно-ориентированных моделей и их отличий.

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

Агрегаты бывают разными. Модели данных типа “ключ—значение” интерпретируют агрегаты как “черный ящик”, т.е. искать можно только целые агрегаты, — вы не може­те подать запрос на извлечение части агрегата.

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

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

 

Рекомендации по теме

Читателям, желающим углубить свои знания об агрегатах, которые часто исполь­зуются и при разработке реляционных баз данных, мы рекомендуем книгу [Evans]. Со­общество специалистов по предметно-ориентированному проектированию является лучшим источником информации об агрегатах, — свежая информация обычно по­является на здесь.

 

Резюме

 

Вас заинтересует / Intresting for you:

Как правильно выбрать базу дан...
Как правильно выбрать базу дан... 7218 просмотров Administrator SU Sun, 07 Oct 2018, 08:31:24
Модели данных и концептуальное...
Модели данных и концептуальное... 2997 просмотров Ирина Светлова Thu, 11 Feb 2021, 14:18:45
Альтернативные модели данных и...
Альтернативные модели данных и... 8613 просмотров Дэйзи ак-Макарова Sun, 09 Sep 2018, 10:39:19
Модели данных и языки запросов
Модели данных и языки запросов 1350 просмотров Дэн Wed, 06 Mar 2019, 16:11:35
Печать
Войдите чтобы комментировать

apv аватар
apv ответил в теме #9869 3 года 1 мес. назад
Из баз данных / хранилищ типа "ключ-значение" кто какие предпочитает? Есть лидеры? Или спецификой задачи все определяется?