Я бы посоветовал вам прочитать этот ответ . Ранее я писал, чтобы предоставить некоторые общие сведения о выборе ключей. Вам также следует открыть ссылки из этого ответа, которые предоставляют основную информацию, доступную 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 . Не уверен, что это поможет.