Разработка эффективных баз данных

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

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

Одна из популярных баз данных для современных приложений - DynamoDB. DynamoDB популярен, потому что он был разработан для огромных случаев использования с высокой скоростью, таких как корзина для покупок Amazon. Таким образом, он не может мириться с непоследовательностью и снижением производительности объединений при масштабировании набора данных.

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

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

Дизайн на основе шаблона доступа

В SQL проектирование моделей основано на нормализации базы данных. Мы конструируем наши данные в соответствии с этими законами: первая нормальная форма, вторая нормальная форма и третья нормальная форма.

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

Нет присоединений

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

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

Создание модели данных в NoSQL на основе диаграммы сущность-взаимосвязь не сработает, и есть разные подходы к моделированию данных в DynamoDB.

Дизайн с одним столом

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

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

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

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

Какова модель доступа вашего приложения?

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

Шаблон доступа - это практически все, что вы думаете о том, как пользователь получает доступ к вашим данным.

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

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

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

Создавайте свои данные с помощью реляционного дизайна данных

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

В примере индекса публикации Medium публикация и заголовок являются отношениями «один ко многим», а заголовок и теги - отношениями «многие ко многим». В каждой публикации будет много статей. Каждая статья может быть во многих тегах, и каждый тег также может быть во многих статьях. Следовательно, если мы хотим спроектировать это в реляционной базе данных, у нас будет четыре таблицы: публикация, статьи, теги и таблица соединения для статей и тегов. Статьи содержат внешний ключ, связанный с публикацией. Таблица соединения содержит tagId, связанный с titleId.

Определение ключа раздела, ключа сортировки и индексов GSI

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

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

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

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

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

Мы создадим значения PK и SK с помощью [EntityName]#[EntityValue]. Например, ключ раздела будет PublicationName#TheBetterProgramming, а ключ сортировки может быть Metadata#[PublicationId], Title#[TitleName], Tag#[Tag]:

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

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

  • Получение публикации: используйте вызов GetItem API и имя публикации, чтобы запросить элемент с PK равным PublicationName#<PublicationName> и SK #Metadata#[PublicationId].
  • Получите все заголовки (теги) в этой публикации, используя действие Query API с ключевым выражением условия PK=PublicationName#<PublicationName> и begins_with(SK, "Title#"("Tag#")). Это позволит получить публикации и заголовок без получения всех Publication объектов и всех тегов.

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

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

Для иллюстрации мы преобразуем модель публикации выше в шаблон составного ключа сортировки. Вместо того, чтобы иметь ключ раздела как PublicationName#<PublicationName>, у нас есть UUID для ключа раздела, и ключ сортировки будет:

  • название публикации: PublicationName#<PublicationName>#Metadata#<PublicationId>
  • название заголовка: PublicationName#<PublicationName>#Title#<TitleName>

У нас может быть несколько шаблонов доступа, например:

  • Получение публикации: используйте API запросов с ключевым выражением условия begins_with(SK,PublicationName#<PublicationName>#Metadata).
  • Получить все заголовки в публикации: API запросов с ключевым выражением условия begins_with(SK, PublicationName#<PublicationName>#Title#)

Однако нам нужно использовать FilterExpression, чтобы вернуть все теги в публикации. Также нам нужно использовать FilterExpression, чтобы получить все публикации без заголовка и тега.

Если вы заметили, размещение поля тега в качестве ключа сортировки Title#[TitleName]#Tag#[TagName] в приведенном выше сценарии может привести к сбою всех имен заголовков и имени тега при поиске.

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

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

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

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

Закрытие

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

В DynamoDB нет операции объединения, как в SQL. Поэтому он предварительно объединяет данные, используя коллекции элементов, чтобы получить все необходимые данные. При такой конструкции рекомендуется использовать дизайн с одним столом.

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

Если вам интересно узнать больше обо всех шаблонах проектирования DynamoDB, ознакомьтесь с этими полезными ресурсами:

Первоначально опубликовано на https://edward-huang.com.