我会鼓励你阅读这个答案。我之前写过一些关于选择键的一般背景。您还应该打开该答案中的链接,这些链接提供了 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。
如果 Books 表中有很大一部分项目的 current_featured_in_book_stores=true,我会进行扫描。如果表中只有少数项目的 current_featured_in_book_stores=true 并且这是一个非常频繁的查询,您可以考虑在 Books 表上创建一个 GSI,分区键为 current_featured_in_book_stores,排序键为 ISBN。
假设您的 books 表有 100 本书,其中 50 本书的 current_featured_in_book_stores=true。进行一次扫描会花费 100 个 RCU,并且不会比一次查询花费更多。现在假设只有一本书的 current_featured_in_book_stores=true,执行一次扫描需要 100 个 RCU,但一次查询只需要 1 个 RCU。但是,您应该在添加 GSI 之前仔细考虑,它们不与基表共享吞吐量,并且您必须为您的 GSI 单独购买 RCU。如果您未配置 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 个。如果用户在缓存范围之外进行分页,您可能需要进行新的扫描。我认为您更有可能只是限制项目数量并停止用户进一步分页。
获取所有类型为“喜剧”的书籍
同样,我很可能会不经常进行扫描并缓存列表。您可以考虑添加具有分区键类型和排序键 ISBN 的 GSI。就我个人而言,我会从扫描和缓存方法开始,看看你是怎么做的。您始终可以在以后添加 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 的图书
对于这个,对每个单独的 ISBN 执行 GetItem 并将其添加到集合中。下面的查询得到一本书。你可以把它放在一个循环中,并遍历你想要获取的 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 映射的想法似乎是一个好主意。只需有一个流派名称和 ID 表,在您的应用程序启动时加载它并将其保存在应用程序内存中。您可能需要一个管理函数来重新加载类型映射表。
将应用程序参数保存在数据库中是一种很好用的设计。
问题 3
当然,在 DynamoDB 中有一个用户表。这就是我在使用 Cognito 的应用程序中执行此操作的方式。我在 Cognito 中存储了一组与用户注册相关的最小字段,然后我在用户表中的 DynamoDB 中有大量应用程序特定的数据。
问题 4
关于图形模式,我会查看AWS 的这篇文章。不太确定这是否有帮助。