asp.net 全局辅助索引的DynamoDB一致性读取

jdzmm42g  于 2023-05-02  发布在  .NET
关注(0)|答案(5)|浏览(168)

为什么我不能获得全局二级索引的一致读取?
我有以下设置:

  • 表:tblUsers(id为hash)
  • 全球二级指数:tblUsersEmailIndex(email作为hash,id作为attribute)
  • 全球二级指数:tblUsersUsernameIndex(用户名为哈希,ID为属性)

我查询索引以检查给定的电子邮件或用户名是否存在,这样就不会创建重复的用户。
现在,问题是我不能对索引上的查询进行一致的读取。但为什么不呢?这是我真正需要最新数据的少数情况之一。
根据AWS文档:
对全局辅助索引的查询仅支持最终一致性。
在正常情况下,对表数据的更改会在几分之一秒内传播到全局辅助索引。然而,在一些不太可能的故障场景中,可能发生更长的传播延迟。因此,您的应用程序需要预测和处理全局二级索引上的查询返回的结果不是最新的情况
但我该怎么处理这种情况呢?如何确保给定的电子邮件或用户名不在数据库中?

htzpubme

htzpubme1#

你可能已经经历过了:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html
简短的回答是,你不能做你想做的事情与全球二级索引(即它总是最终的一致性)。
这里的一个解决方案是将您感兴趣的属性作为一个键,并在那里进行一致的读取。你需要确保你正在更新,每当你插入新的实体,你也必须担心的边缘情况下,插入成功,但不是在主表(即你需要确保他们是同步的)
另一种解决方案是扫描整个表,但如果表很大,那么这可能是多余的。
你为什么要关心有人用同一个电子邮件创建了两个帐户?您可以只使用用户名作为主哈希键,而不强制电子邮件的唯一性。

ar7v8xwq

ar7v8xwq2#

当您尝试使用putItem时,您可以使用ConditionExpression来检查是否满足放置项目的条件,这意味着您可以检查emailusername是否存在。

ConditionExpression — (String)
A condition that must be satisfied in order for a conditional PutItem operation to succeed.

An expression can contain any of the following:

Functions: attribute_exists | attribute_not_exists | attribute_type | contains | begins_with | size
These function names are case-sensitive.

Comparison operators: = | <> | < | > | <= | >= | BETWEEN | IN
Logical operators: AND | OR | NOT
For more information on condition expressions, see Condition Expressions in the Amazon DynamoDB Developer Guide.

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property

vmdwslir

vmdwslir3#

我最近遇到了这个问题,想分享一个更新。2018年,DynamoDB增加了事务。如果你真的需要保持两个项目(在相同或不同的表中)100%同步,而不需要担心最终的一致性,TransactWriteItems和TransactGetItems就是你所需要的。
最好是完全避免交易,如果可以的话,就像其他人建议的那样。

ncecgwcz

ncecgwcz4#

你不可能在GSI上有强一致的读数。你能做的是
将您的模式建模为具有2行,e。g:-

user#uId as pk.
email#emailId as pk.
make pk as of type string
kq4fsx7k

kq4fsx7k5#

根据您的情况并考虑所有替代方案,当您第一次在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;
}

由于我们不关心发送不存在的令牌的人的性能(无论如何都不应该发生),因此我们在没有任何性能损失的情况下解决了这个问题(除了在令牌创建后可能有一次1秒的延迟)。如果您刚刚创建了令牌,则不需要将其解析回刚刚传入的数据。但如果你这样做了,我们会以透明的方式处理。

相关问题