Следует ли моделировать отношения, используя AWS Appsync с DynamoDB, путем хранения избыточных копий связанных данных в одной таблице (денормализация)?

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

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

Вариант 1. Для меня очевидный способ смоделировать это в AppSync и DynamoDB - это начать с таблицы ingredients, в которой есть один элемент для каждого ингредиента, в котором хранятся все данные ингредиентов, с ingredient id в качестве ключа раздела. Затем у меня есть еще одна таблица recipes с ключом разделения recipe id и поле ingredients, в котором хранятся все ingredient id в массиве. В AppSync я мог затем запросить рецепт, выполнив запрос GetItem с помощью recipe id, а затем разрешив поле ingredients с помощью BatchGetItem в таблице ingredients. Скажем, рецепт содержит в среднем 10 ингредиентов, так что это будет означать, что к таблицам DynamoDB будет отправлено 11 запросов GetItem.

Вариант 2: Я бы счел эту операцию «подобной соединению», которая, по-видимому, не является идеальным способом использования нереляционных баз данных. Итак, в качестве альтернативы я мог бы сделать следующее: Сделать «избыточные копии» всех данных ингредиентов в таблице recipes и не только сохранить там ingredient id, но также и все другие данные из таблицы ingredients. Это может значительно увеличить использование дискового пространства, но очевидно, что дисковое пространство дешевое, и повышение производительности за счет выполнения только 1 запроса GetItem (вместо 11) может того стоить. Как описано далее в руководстве по ElasticSearch, это также потребует некоторой дополнительной работы для обеспечения параллелизма при обновлении данных ингредиентов. Поэтому мне, вероятно, придется использовать поток DynamoDB для обновления всех данных в таблице recipes, когда обновляется ингредиент. Это потребует дорогостоящего сканирования, чтобы найти все рецепты с использованием обновленного ингредиента, и BatchWrite для обновления всех этих элементов. (Однако обновление ингредиентов может быть редким, поэтому увеличение производительности чтения может того стоить.)

Мне было бы интересно услышать ваше мнение по этому поводу:

  • Какой вариант вы бы выбрали и почему?
  • Второй «более нереляционный способ» сделать это кажется болезненным, и я обеспокоен тем, что с появлением большего количества уровней / отношений (например, если пользователи могут создавать меню из рецептов), возникающая сложность может быстро выйти из-под контроля, когда я для многократного сохранения «дублирующих копий» одних и тех же данных. Я мало что знаю о реляционных базах данных, но там эти вещи кажутся намного проще, когда все данные имеют свое уникальное местоположение и все (я думаю, это то, что означает «нормализация»).
  • Действительно ли getRecipe вариант 1 в 11 раз дороже (с точки зрения производительности и стоимости), чем вариант 2? Или я что-то неправильно понял?
  • Будет ли вариант 1 более дешевой операцией в реляционной базе данных (например, MySQL), чем в DynamoDB? Несмотря на то, что это соединение, если я правильно понимаю, это всего лишь 11 («предполагаемый способ NoSQL») операций GetItem. Может ли это быть быстрее, чем 1 запрос SQL?
  • Если у меня очень реляционная структура данных, может ли нереляционная база данных, такая как DynamoDB, быть плохим выбором? Или AppSync / GraphQL - это способ сделать его жизнеспособным выбором (разрешив вариант 1, который действительно легко создать)? Я читал некоторые мнения, которые постоянно работают над отсутствием возможности соединения при запросе к базам данных NoSQL, и необходимость делать это на стороне приложения является основной причиной, по которой это не подходит. Но AppSync может помочь решить эту проблему. В других мнениях (включая документы DynamoDB) проблемы производительности упоминаются как основная причина, по которой вы всегда должны запрашивать только одну таблицу.

person tinymarsracing    schedule 11.10.2018    source источник


Ответы (1)


Я знаю, что это уже довольно поздно, но может помочь кому-то в будущем.

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

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

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

TL; DR предназначен для моделирования всей диаграммы взаимосвязей сущностей вашего приложения и продумывания всех шаблонов доступа. Если отношение один ко многим, вы можете денормализовать данные, использовать составной ключ сортировки или использовать вторичные индексы. Если «многие ко многим», вы начинаете изучать списки смежности и другие продвинутые стратегии. У Алекса Дебри есть несколько отличных ресурсов: здесь и здесь.

person beevor    schedule 28.07.2020