Как моделировать отношения «один к одному», «один ко многим» и «многие ко многим» в DynamoDB - PullRequest
5 голосов
/ 14 марта 2019

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

  • Отношения один-к-одному
  • Отношения один-ко-многим
  • Многие-отношения ко многим

1 Ответ

11 голосов
/ 14 марта 2019

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

Основные сведения об DynamoDB

Прежде чем читать это, вы должны понять:

  • Каждая таблица DynamoDB имеет уникальный первичный ключ
  • Первичный ключ должен состоять из ключа раздела и может дополнительно иметь ключ сортировки . Первичный ключ с ключом разделения и ключом сортировки - это составной ключ .
  • A GetItem запрос возвращает один и только один элемент, используя его уникальный первичный ключ.
  • A Запрос выполняет быстрый поиск и должен указывать один и только один ключ раздела. Может вернуть несколько предметов.
  • A Сканирование оценивает каждый элемент в таблице и может возвращать подмножество на основе параметров фильтра. Сканирование является правильным выбором в некоторых обстоятельствах, но может быть медленным и дорогостоящим при неправильном использовании.
  • Разница между GSI и LSI и тем, для чего они используются.

Один-на-один


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

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

Таблица паспортов:

Ключ раздела: PassportId

╔════════════╦═══════╦════════════╗
║ PassportId ║ Pages ║   Issued   ║
╠════════════╬═══════╬════════════╣
║ P1         ║    15 ║ 11/03/2009 ║
║ P2         ║    18 ║ 09/02/2018 ║
╚════════════╩═══════╩════════════╝

Таблица владельца паспорта:

Ключ раздела: PersonId

╔══════════╦════════════╦══════╗
║ PersonId ║ PassportId ║ Name ║
╠══════════╬════════════╬══════╣
║ 123      ║ P1         ║ Jane ║
║ 234      ║ P2         ║ Paul ║
╚══════════╩════════════╩══════╝

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

Однако нам не хватает варианта использования. Мы можем легко найти человека по его PersonId и выяснить, какой у него паспорт. Но что, если у нас есть PassportId и нам нужно найти, кому он принадлежит? В текущей модели нам нужно выполнить Сканирование на столе владельца паспорта. Если это обычный случай использования, мы не хотим использовать сканирование. Для поддержки GetItem мы можем просто добавить GSI к таблице владельцев паспортов:

Таблица владельца паспорта GSI:

Ключ раздела: PassportId

╔════════════╦══════════╦══════╗
║ PassportId ║ PersonId ║ Name ║
╠════════════╬══════════╬══════╣
║ P1         ║ 123      ║ Jane ║
║ P2         ║ 234      ║ Paul ║
╚════════════╩══════════╩══════╝

Теперь мы можем искать отношения с помощью PassportId или PersonId очень быстро и дешево.

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


Один-ко-многим


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

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

Стол для домашних животных:

Ключ раздела: PetId

╔═══════╦═════════╦════════╗
║ PetId ║ OwnerId ║ Type   ║
╠═══════╬═════════╬════════╣
║ P1    ║ O1      ║ Dog    ║
║ P2    ║ O1      ║ Cat    ║
║ P3    ║ O2      ║ Rabbit ║
╚═══════╩═════════╩════════╝

Таблица владельца:

Ключ раздела: OwnerId

╔═════════╦════════╗
║ OwnerId ║ Name   ║
╠═════════╬════════╣
║ O1      ║ Angela ║
║ O2      ║ David  ║
╚═════════╩════════╝

Мы помещаем внешний ключ в таблицу many . Если бы мы сделали это наоборот и поместили PetIds в таблицу Owner, у одного элемента Owner должен был бы быть набор PetIds, и им было бы сложно управлять.

Если мы хотим найти владельца для питомца, это очень просто.Мы можем сделать GetItem , чтобы вернуть Предмет питомца, и он говорит нам, кто является владельцем.Но наоборот все труднее - если у нас есть OwnerId, какими домашними животными они владеют?Чтобы спасти нас, нужно сделать сканирование на столе питомца, вместо этого мы добавляем GSI к столу питомца.

стол питомца GSI

Ключ раздела: OwnerId

╔═════════╦═══════╦════════╗
║ OwnerId ║ PetId ║ Type   ║
╠═════════╬═══════╬════════╣
║ O1      ║ P1    ║ Dog    ║
║ O1      ║ P2    ║ Cat    ║
║ O2      ║ P3    ║ Rabbit ║
╚═════════╩═══════╩════════╝

Если у нас есть OwnerId, и мы хотим найти их питомцев, мы можем выполнить Запрос на GSI таблицы питомцев.Например, запрос на владельца O1 вернет элементы с PetId P1 и P2.

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

В таблице DynamoDB каждое значение ключа должно быть уникальным.Однако значения ключей в глобальном вторичном индексе не обязательно должны быть уникальными

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


Многие ко многим


Существует три основных способасмоделируйте отношения многие ко многим в DynamoDB.У каждого есть свои сильные и слабые стороны.

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


Многие ко многим - Вариант 1 - Вспомогательный стол


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

Врачебный стол:

РазделениеКлюч: DoctorId

╔══════════╦═══════╗
║ DoctorId ║ Name  ║
╠══════════╬═══════╣
║ D1       ║ Anita ║
║ D2       ║ Mary  ║
║ D3       ║ Paul  ║
╚══════════╩═══════╝

Таблица пациентов

Ключ раздела: PatientId

╔═══════════╦═════════╦════════════╗
║ PatientId ║ Name    ║ Illness    ║
╠═══════════╬═════════╬════════════╣
║ P1        ║ Barry   ║ Headache   ║
║ P2        ║ Cathryn ║ Itchy eyes ║
║ P3        ║ Zoe     ║ Munchausen ║
╚═══════════╩═════════╩════════════╝

Таблица DoctorPatient (вспомогательная таблица)

Ключ раздела: DoctorId

Ключ сортировки: PatientId

╔══════════╦═══════════╦══════════════╗
║ DoctorId ║ PatientId ║ Last Meeting ║
╠══════════╬═══════════╬══════════════╣
║ D1       ║ P1        ║ 01/01/2018   ║
║ D1       ║ P2        ║ 02/01/2018   ║
║ D2       ║ P2        ║ 03/01/2018   ║
║ D2       ║ P3        ║ 04/01/2018   ║
║ D3       ║ P3        ║ 05/01/2018   ║
╚══════════╩═══════════╩══════════════╝

Таблица врача-пациента GSI

Ключ раздела: PatientId

Ключ сортировки: DoctorId

╔═══════════╦══════════╦══════════════╗
║ PatientId ║ DoctorId ║ Last Meeting ║
╠═══════════╬══════════╬══════════════╣
║ P1        ║ D1       ║ 01/01/2018   ║
║ P2        ║ D1       ║ 02/01/2018   ║
║ P2        ║ D2       ║ 03/01/2018   ║
║ P3        ║ D2       ║ 04/01/2018   ║
║ P3        ║ D3       ║ 05/01/2018   ║
╚═══════════╩══════════╩══════════════╝

Есть три таблицы, интересной является вспомогательная таблица DoctorPatient.

Первичный ключ базовой таблицы DoctorPatient должен быть уникальным, поэтому мы создаем составной ключ DoctorId (ключ раздела) и PatientId (ключ сортировки).

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

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

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

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

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


Многие ко многим - Вариант 2 - Наборы внешних ключей


Этот подход использует только дватаблицы.

Стол врача:

Ключ раздела: DoctorId

╔══════════╦════════════╦═══════╗
║ DoctorId ║ PatientIds ║ Name  ║
╠══════════╬════════════╬═══════╣
║ D1       ║ P1,P2      ║ Anita ║
║ D2       ║ P2,P3      ║ Mary  ║
║ D3       ║ P3         ║ Paul  ║
╚══════════╩════════════╩═══════╝

Таблица пациента:

Ключ раздела: PatientId

╔═══════════╦══════════╦═════════╗
║ PatientId ║ DoctorIds║  Name   ║
╠═══════════╬══════════╬═════════╣
║ P1        ║ D1       ║ Barry   ║
║ P2        ║ D1,D2    ║ Cathryn ║
║ P3        ║ D2,D3    ║ Zoe     ║
╚═══════════╩══════════╩═════════╝

Этот подход предполагает сохранение отношений в виде набора в каждой таблице.

Чтобы найти «Пациентов для доктора», мы можем использовать GetItem на столе «Доктор», чтобы получить элемент «Доктор». Затем PatientIds сохраняются как набор в атрибуте Doctor.

Чтобы найти Доктора для Пациента, мы можем использовать GetItem в таблице Пациента, чтобы получить элемент Пациент. Затем DoctorIds сохраняются как набор в атрибуте Patient.

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

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

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


Многие ко многим - Вариант 3 - Схема графика


AWS ранее упоминал это как шаблон списка смежности . Чаще всего его называют База данных графиков или Triple Store .

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

А есть недавняя презентация AWS, в которой много говорится об этом паттерне здесь

Подход предполагает размещение всех данных в одной таблице.

Я только что нарисовал несколько примеров строк, а не всю таблицу:

Ключ раздела: Key1

Ключ сортировки: ключ2

╔═════════╦═════════╦═══════╦═════════════╦══════════════╗
║ Key1    ║ Key2    ║ Name  ║   illness   ║ Last Meeting ║
╠═════════╬═════════╬═══════╬═════════════╬══════════════╣
║ P1      ║ P1      ║ Barry ║ Headache    ║              ║
║ D1      ║ D1      ║ Anita ║             ║              ║
║ D1      ║ P1      ║       ║             ║ 01/01/2018   ║
╚═════════╩═════════╩═══════╩═════════════╩══════════════╝

И затем требуется GSI, который инвертирует ключи:

Ключ раздела: Key2

Ключ сортировки: Ключ1

╔═════════╦═════════╦═══════╦═════════════╦══════════════╗
║ Key2    ║ Key1    ║ Name  ║   illness   ║ Last Meeting ║
╠═════════╬═════════╬═══════╬═════════════╬══════════════╣
║ P1      ║ P1      ║ Barry ║ Headache    ║              ║
║ D1      ║ D1      ║ Anita ║             ║              ║
║ P1      ║ D1      ║       ║             ║ 01/01/2018   ║
╚═════════╩═════════╩═══════╩═════════════╩══════════════╝

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

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

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

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

Короче говоря, я думаю, что AWS, вероятно, выпустила то, что должно было стать полезной статьей в ряде статей, но, не сумев представить другие (более простые) концепции для управления отношениями «многие ко многим», они запутали многих людей. Итак, чтобы быть ясным, шаблон списка смежности может быть полезен, но его не единственный вариант для моделирования отношений «многие ко многим» в DynamoDB. Обязательно используйте его, если он подходит для ваших обстоятельств, таких как серьезные большие данные, но если нет, попробуйте одну из более простых моделей.

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