DynamoDB 更新是否高度一致?

Are DynamoDB Updates strongly consistent?

DynamoDB 快速且可扩展的全部原因在于它最终是一致的。但与此同时,它带有此 ConsistentRead 选项,用于 getbatchGetquery 等操作,可帮助您确保正在读取的数据是最新的。

我的问题是关于 update 操作。首先,它没有 ConsistentRead 选项(一个原因是,update 不是读!)。但与此同时,您可以使用 ConditionExpression 以原子方式更新记录,如下所示:

await docClient.update({
    TableName: 'SomeTable',
    Key: {id},
    UpdateExpression: "set #status = :new_status",
    ConditionExpression: '#status = :old_status',
    ExpressionAttributeNames: {
        "#status": "status",
    },
    ExpressionAttributeValues: {
        ":old_status": "available",
        ":new_status": "done",
    },
}).promise()

这将确保在更新时,旧值是 available,如果不是,操作将失败并抛出异常。所以,从某种意义上说,你可以说 update 是强一致的。

但我的问题是关于您需要确保记录存在的场景。假设您有一个插入记录的函数。另一个更新相同的记录(给定它的 id)。我担心的是,如果在 update 操作执行时,由于 DynamoDB 的最终一致性,没有匹配的记录并且更新失败。如前所述,update 操作不附带 ConsistentRead 选项以使其高度一致。

这是一个有效的问题吗?有什么我可以做的吗?

没有强一致性更新;强一致性适用于读取,其中基本上在写入后立即查看的数据对于实体的所有观察者都是一致的。

当您的应用程序将数据写入 DynamoDB table 并收到 HTTP 200 响应(OK)时,写入已发生(在 至少一个存储位置耐用。数据在所有存储位置最终一致,通常在一秒或更短时间内。然后,您可以选择以最终一致性或强一致性方式读取此数据。

对同一项目的并发写入应使用开放式并发处理,您可以使用 DynamoDB 事务库(在 AWS SDK 中为 Java 提供条件写入 ).

如果您需要以原子方式更新多个项目,您可以使用 DynamoDB 事务。

DynamoDB transactions provide developers atomicity, consistency, isolation, and durability (ACID) across one or more tables within a single AWS account and region. You can use transactions when building applications that require coordinated inserts, deletes, or updates to multiple items as part of a single logical business operation.

https://aws.amazon.com/blogs/aws/new-amazon-dynamodb-transactions/

CRUD 操作是原子的;然而,官方文档并未提及它们被隔离(在 DynamoDB 事务之外)。原则上,竞争条件可能会发生,条件更新可能会return出错。

在此处查看更多信息:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html

或者,您的用例可能受益于 DynamoDB 全局 tables,它在并发写入之间使用“最后写入者获胜”协调。

这个问题过去在这里被问过很多次,例如见

Are dynamodb update expressions strongly consistent?

Are DynamoDB conditional writes strongly consistent?

上面接受的答案 more-or-less 表明,在作为写入的一部分发生的读取中,所有的赌注都没有了,你不能相信它们是一致的——所以你需要使用新的“DynamoDB 事务“ 特征。但我认为那个答案的结论是错误的。当您有一个事务需要隔离对几个不同项目的写入并安全地支持失败的 non-idempotent 写入时,就需要新的“DynamoDB 事务”。但在UpdateItem支持的single-item更新中,我相信涉及的reads-before-write实际上是强一致:

不幸的是,DynamoDB 的文档对此并不完全清楚,但这里有一些来自 DynamoDB 文档的证据来支持我的信念:

  1. UpdateItem 可能需要读取项目的旧值(so-called read-before-write)的原因有多种 - 问题是这些读取的一致性.读取的原因之一是 UpdateExpression(例如,更新可以增加属性),另一个是 ConditionExpression(更新可以以属性的旧值为条件),第三个案例是 ReturnValues - 用户要求获取项目的旧值。尽管UpdateItem documentation对前两种情况的一致性不清楚,但对第三种情况的一致性非常明确:“返回的值是强一致的。”.我看不出为什么 read-before-write 在这种情况下会是 strongly-consistent,而在其他情况下则不是 strongly-consistent,所以我相信在所有三种情况下都是 strongly-consistent。
  2. 来自 DynamoDB 的 DynamoDB 内部结构的几个演示解释了它在内部是如何工作的:它为每条数据保留三个副本,并且在任何时候三个副本中的一个被认为是“领导者”;写入总是首先到达领导者并在领导者和第二个副本成功时成功,eventual-consistency 读取随机进入三个副本之一(因此可能从一个尚未收到写入的副本读取)但是 strongly-consistent 阅读总是交给领导者。这意味着 strongly-consistent 读取和写入到同一个副本(领导者),并具有相同的一致性保证。此外,领导者线性化了对该项目的写入(即按某种顺序执行,而不是同时进行),因此一次写入的 read-before-write 可以看到之前写入同一项目的所有内容。