为什么我无法获得全局二级索引的一致读取?
我有以下设置:
表:tblUsers
(id为哈希)
全球二级索引:tblUsersEmailIndex
(电子邮件为哈希,id为属性)
全球二级索引:tblUsersUsernameIndex
(用户名为哈希,id为属性)
我查询索引以检查是否存在给定的电子邮件或用户名,因此我不会创建重复的用户。
现在,问题是我无法对索引的查询进行一致读取。但为什么不呢?这是我实际上需要最新数据的少数几次之一。
根据AWS文档:
全局二级索引的查询仅支持最终一致性。
在正常情况下,表数据的更改会在几分之一秒内传播到全局二级索引。但是,在某些不太可能发生的故障情况下,可能会出现较长的传因此,您的应用程序需要预测并处理全局二级索引上的查询返回的结果不是最新的情况。
但我如何处理这种情况呢?如何确保数据库中不存在给定的电子邮件或用户名?
答案 0 :(得分:1)
你可能已经完成了这个: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html
简短的回答是,你不能用全局二级索引做你想做的事情(即它总是最终的一致性)。
这里的解决方案是将一个单独的表与您感兴趣的属性作为键并在那里进行一致的读取。您需要确保在插入新实体时更新它,并且您还必须担心插入成功的边缘情况,但不在主表中(即您需要确保它们同步)
另一个解决方案是扫描整个表格,但如果表格很大,那可能会有点过分。
为什么有人在使用同一封电子邮件创建2个帐户?您可以只使用用户名作为主哈希密钥,而不是强制执行电子邮件唯一性。
答案 1 :(得分:0)
根据您的情况并考虑所有其他选择,当您第一次在GSI上找不到任何内容时,可以尝试添加自动重试来解决缺少强一致性读取的问题。我什至没有想到这一点,直到我遇到其他选择时遇到障碍,然后才意识到这很简单,并且不会对我们的特定用例造成任何问题。
{
"TableName": "tokens",
"ProvisionedThroughput": { "ReadCapacityUnits": 5, "WriteCapacityUnits": 5 },
"AttributeDefinitions": [
{ "AttributeName": "data", "AttributeType": "S" },
{ "AttributeName": "type", "AttributeType": "S" },
{ "AttributeName": "token", "AttributeType": "S" }
],
"KeySchema": [
{ "AttributeName": "data", "KeyType": "HASH" },
{ "AttributeName": "type", "KeyType": "RANGE" }
],
"GlobalSecondaryIndexes": [
{
"IndexName": "tokens-token",
"KeySchema": [
{ "AttributeName": "token", "KeyType": "HASH" }
],
"Projection": {
"ProjectionType": "ALL"
},
"ProvisionedThroughput": { "ReadCapacityUnits": 2, "WriteCapacityUnits": 2 }
}
],
"SSESpecification": {"Enabled": true }
}
public async getByToken(token: string): Promise<TokenResponse> {
let tokenResponse: TokenResponse;
let tries = 1;
while (tries <= 2) { // Can't perform strongly consistent read on GSI so we have to do this to insure the token doesn't exist
let item = await this.getItemByToken(token);
if (item) return new TokenResponse(item);
if (tries == 1) await this.sleep(1000);
tries++;
}
return tokenResponse;
}
由于我们不关心发送不存在的令牌的人的性能(无论如何也永远不会发生),因此我们可以在不影响性能的情况下解决此问题(除了令牌已创建)。如果您只是创建令牌,则无需将其解析回您刚刚传入的数据。但是,如果您恰好这样做,我们将透明地对其进行处理。
答案 2 :(得分:0)
当您尝试使用putItem
时,您可以使用ConditionExpression
来检查是否满足放置物品的条件,这意味着您可以检查email
或{ {1}}存在。
username
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property
答案 3 :(得分:0)
我最近遇到了这个问题,想分享一个更新。 2018 年,DynamoDB 增加了事务。如果您真的需要保持两个项目(在相同或不同的表中)100% 同步而无需担心最终的一致性,那么 TransactWriteItems 和 TransactGetItems 就是您所需要的。
如果可以的话,最好完全避免交易,正如其他人所建议的那样。