到目前为止,我们一直关注如何根据键属性访问数据。如果我们想根据非键属性查找项目,我们必须进行完整的表扫描并使用过滤条件来找到我们想要的内容,这对于大规模运营的系统来说既非常慢又非常昂贵。
DynamoDB提供了一个名为全局二级索引(GSI) 的功能,它可以自动将我们的数据围绕不同的分区和排序键进行转换。数据可以重新分组和重新排序,以允许更多的访问模式通过查询和扫描API快速提供服务。
回想一下之前的示例,我们想找到Reply表中由用户A发布的所有回复:
aws dynamodb scan \
--table-name Reply \
--filter-expression 'PostedBy = :user' \
--expression-attribute-values '{
":user" : {"S": "User A"}
}' \
--return-consumed-capacity TOTAL
当运行该扫描操作时,我们可以看到返回的Count与ScannedCount不同。如果有10亿条回复项目,但只有3条是由用户A发布的,我们将不得不支付(时间和金钱)来扫描10亿个项目,只为找到我们想要的3个。
有了对GSI的这些了解,我们现在可以在Reply表上创建一个GSI来服务这个新的访问模式。 GSI可以随时创建和删除,即使表中已经有数据。
这个新的GSI将使用PostedBy属性作为分区(HASH)键,我们仍然将消息按ReplyDateTime
排序作为排序(RANGE)键。 我们希望将表中的所有属性复制(投影)到GSI中,因此我们将使用ALL投影类型。 请注意,我们创建的索引名称为PostedBy-ReplyDateTime-gsi
。
aws dynamodb update-table \
--table-name Reply \
--attribute-definitions AttributeName=PostedBy,AttributeType=S AttributeName=ReplyDateTime,AttributeType=S \
--global-secondary-index-updates '[{
"Create":{
"IndexName": "PostedBy-ReplyDateTime-gsi",
"KeySchema": [
{
"AttributeName" : "PostedBy",
"KeyType": "HASH"
},
{
"AttributeName" : "ReplyDateTime",
"KeyType" : "RANGE"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 5, "WriteCapacityUnits": 5
},
"Projection": {
"ProjectionType": "ALL"
}
}
}
]'
在DynamoDB创建GSI并从表中回填数据到索引中可能需要一些时间。 我们可以从命令行观察并等待IndexStatus变为ACTIVE
:
#获取状态
aws dynamodb describe-table --table-name Reply --query "Table.GlobalSecondaryIndexes[0].IndexStatus"
一旦GSI变为ACTIVE
,继续进行下面
找到由用户A编写的所有回复,并按顺序排列。
在DynamoDB中使用GSI时,我们必须明确告诉DynamoDB表名和索引名。 对GSI运行query
与对表运行没有什么不同,只是我们还需要使用--index-name
选项指定要使用哪个GSI,并将GSI键属性用于KeyConditionExpression。
aws dynamodb query \
--table-name Reply \
--key-condition-expression 'PostedBy = :pb' \
--expression-attribute-values '{
":pb" : {"S": "User A"}
}' \
--index-name PostedBy-ReplyDateTime-gsi \
--return-consumed-capacity TOTAL
注意输出中:
"Count": 3,
"ScannedCount": 3,
query
无法比这更优化。即使表中有10亿条由其他用户发布的Reply项,这个查询也只会花费我们读取我们希望返回的3个确切项目(与scan
不同)。
aws dynamodb update-table \
--table-name Reply \
--global-secondary-index-updates '[{
"Delete":{
"IndexName": "PostedBy-ReplyDateTime-gsi"
}
}
]'