DynamoDB: Лучшие ключи хеша / сортировки для моего варианта использования [путаница с AppSync / GraphQL] - PullRequest
0 голосов
/ 06 мая 2018

Я планирую использовать AWS Cognito для аутентификации пользователей, DynamoDB для персистентности и AppSync (и множество мобильных хабов) для поддержки API - сайт Книжного обозрения .

Мне трудно определить, какое поле должно быть моим хеш-ключом, а какое должно быть моим ключом сортировки, и какую LSI / GSI я должен создать.

У меня есть список книг с такими подробностями:

type Book {
  isbn: Int!
  year: Int!
  title: String!
  description: String
  front_cover_photo_url: String
  genre_ids: [Int]
  count_thumbs: Int
  us_release_date: String
  upcoming_release: Boolean
  currently_featured_in_book_stores: Boolean
  best_seller: Boolean
  reviews: [Review]
}

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

type Review {
  isbn: Int!
  id: ID!
  created_at: String!

  # The user that submitted the review
  user_id: String!

  # The number of thumbs out of 5
  thumbs: Int!

  # Comments on the review
  comments: String!
}

Книги, в моем случае, могут иметь несколько жанров - например, «Фэнтези» и «Драма». Книги также имеют отзывы пользователей, чьи данные хранятся в Cognito. Мы будем отображать рецензии в обратном хронологическом порядке рядом с каждой книгой.

ВОПРОС 1: Если я денормализую и использую Drama в качестве жанра вместо идентификатора жанра 2, то что если мне нужно переименовать жанр позже в Dramatic ... не понадобится ли мне обновить каждый элемент?

Мне нужно ответить как минимум:

  • Получить все книги, представленные в настоящее время в книжных магазинах [currently_featured_in_book_stores == True]
  • Получить все книги, которые "выходят" [upcoming_release == True]
  • Получить все книги отсортированы по большинству больших пальцев [сортировать по count_thumbs DESC]
  • Получить все книги в жанре "Комедия" [genre_ids содержит 123 или "Комедия" в зависимости от ответа на Q1 ]
  • Запрос для книг по имени "Гарри Поттер" [title НРАВИТСЯ '% Harry Potter%']
  • Получить все книги с номерами ISBN 1, 2, 3, 4 или 9 [isbn IN [1,2,3,4,9]] *

ВОПРОС 2: Какой наилучший способ структурировать данные книги в DynamoDB и какой хеш / сортировка / LSI / GSI вы бы использовали?

Поскольку я использую Cognito, данные профиля пользователя хранятся вне DynamoDB.

ВОПРОС 3: Должен ли я иметь таблицу User в DynamoDB и двойную запись новых регистраций, чтобы я мог использовать AppSync для заполнения деталей обзора при отображении его обзора? Если нет, как я могу получить имя пользователя / имя / фамилию пользователя при заполнении сведений о рецензии на книгу?

ВОПРОС 4: Поскольку мы зашли так далеко, есть какие-нибудь предложения для схемы graphql?

1 Ответ

0 голосов
/ 08 мая 2018

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

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

Вопрос 2

Тем не менее, вот что я хотел бы сделать в вашем случае. Я бы посмотрел на создание таблицы с именем Books и таблицы с именем BookReviews.

Table: Books
Partition Key: ISBN

Table: BookReviews
Partition Key: ISBN
Sort Key: BookReview-id

Я бы не стал создавать GSI или LSI.

Большинство ваших запросов связаны с поиском «всех книг» и их упорядочением. Эти списки не чувствительны ко времени. Например, когда пользователь запрашивает 100 самых популярных книг, нужно ли ему знать самые популярные книги, включая каждый подсчитанный голос до последней секунды? Я сомневаюсь. Кроме того, эти списки специфичны для отдельных пользователей? Это не похоже на это.

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

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

 var params = {
  TableName: "Books",
  ExpressionAttributeValues: {
   ":a": {
     BOOL: true
    }
  }, 
  FilterExpression: "currently_featured_in_book_stores = :a"
 };
 dynamodb.scan(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

Эта операция извлечет все книги, которые в настоящее время представлены в книжных магазинах. Он использует скан . Если вы еще не знакомы с scan , query и getItem , вам определенно стоит потратить некоторое время на чтение о них.

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

Если на вашем столе было 100 предметов, ЛЮБОЕ сканирование, которое вы выполняете, будет стоить 100 RCU. Если вы выполняете запрос, и в запрашиваемом разделе находятся только 2 элемента, это будет стоить вам 2 RCU.

Если значительная доля элементов в таблице Книги в настоящее время имеет значение true_featured_in_book_stores = true, я бы сделал сканирование. Если только небольшое количество элементов в таблице в настоящее время имеет значение current_featured_in_book_stores = true и это очень частый запрос, вы можете подумать о создании GSI для таблицы Books с ключом раздела current_featured_in_book_stores и ключом сортировки ISBN.

Представьте, что в вашей таблице книг 100 книг, а у 50 - now_featured_in_book_stores = true. Сканирование стоит 100 RCU и не будет стоить намного дороже, чем запрос. Теперь представьте, что только одна книга в настоящее время имеет значение true_featured_in_book_stores = true, выполнение сканирования обойдется в 100 RCU, но запрос будет стоить только 1 RCU. Однако вы должны хорошо подумать, прежде чем добавлять GSI, поскольку они не делят пропускную способность с базовой таблицей, и вам придется приобретать RCU отдельно для GSI. Если вы предоставляете GSI, он может оказаться медленнее, чем сканирование на хорошо подготовленной базовой таблице.

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

 var params = {
  TableName: "Books",
  IndexName: "Index_Books_In_Stores",
  ExpressionAttributeValues: {
   ":v1": {
     BOOL: true
    }
  }, 
  KeyConditionExpression: "currently_featured_in_book_stores = :v1"
 };
 dynamodb.query(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

Получить все предстоящие книги

Все вышеперечисленное все еще применяется. Я бы сделал сканирование, как это

var params = {
  TableName: "Books",
  ExpressionAttributeValues: {
   ":a": {
     BOOL: true
    }
  }, 
  FilterExpression: "upcoming_release = :a"
 };
 dynamodb.scan(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

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

Получить все книги отсортированы по наибольшему количеству

Важная вещь здесь - «Получить все книги ...». Это сразу говорит вам, что сканирование, вероятно, будет лучшим способом. Вы можете рассматривать запрос как сканирование, которое просматривает только один раздел. Вы не хотите смотреть на раздел книг, вам нужны ВСЕ книги, поэтому сканирование - это путь.

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

 var params = {
  TableName: "Books"
 };
 dynamodb.scan(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

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

Получить все книги в жанре "Комедия"

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

Запрос для книги (книг) с именем "Гарри Поттер"

Очевидно, что вы не можете кешировать это. Сканирование с фильтрацией по названию

 var params = {
  TableName: "Books",
  ExpressionAttributeValues: {
   ":a": {
     S: "Harry Potter"
    }
  }, 
  FilterExpression: "title CONTAINS :a"
 };
 dynamodb.scan(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

Вы можете проверить операторы условия здесь

Получить все книги с ISBN 1, 2, 3, 4 или 9

Для этого выполните GetItem для каждого отдельного ISBN и добавьте его в набор. Запрос ниже получает одну книгу. Вы бы поместили это в цикл и перебрали набор ISBN, которые вы хотите получить.

 var params = {
  Key: {
   "ISBN": {
     S: "1"
    }
  }, 
  TableName: "Books"
 };
 dynamodb.getItem(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });

Вопрос 1

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

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

Хранение параметров приложения в базе данных - хорошо используемый дизайн.

Вопрос 3

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

Вопрос 4

Что касается графовых схем, я бы проверил этой статьи от AWS . Не уверен, что это поможет.

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