DynamoDB 并发更新,有保障吗?

Nad*_*'El 9 amazon-dynamodb

一般来说,如果我想确定当多个线程对 DynamoDB 中的同一项目进行并发更新时会发生什么,我应该使用条件更新(即“乐观锁定”)。我知道。但是我想知道是否还有其他情况可以确保对同一项目的并发更新仍然存在。

例如,在 Cassandra 中,对同一项目的不同属性进行并发更新是可以的,并且两个更新最终都可以读取。在 DynamoDB 中也是如此吗?或者是否有可能只有其中一个更新幸存下来?

一个非常相似的问题是,如果我同时将两个不同的值添加到同一项目的集合或列表中会发生什么。我是否保证在我阅读此集合或列表时最终会看到这两个值,或者在某种 DynamoDB“冲突解决”协议期间,其中一个添加项是否可能掩盖另一个?

我看到我的第二个问题的一个版本过去已经在这里问过了DynamoDB 是“设置”值 CDRT 吗?,但答案参考了一个不太清楚的常见问题解答条目,该条目不再存在。作为我的问题的答案,我最希望看到的是 DynamoDB 官方文档,该文档说明了 DynamoDB 在不涉及“条件更新”和“事务”时如何处理并发更新,尤其是上述两个示例中发生的情况。如果没有这样的官方文档,有没有人对这种并发更新有任何实际经验?

pol*_*ene 6

我只是有同样的问题,并遇到了这个线程。鉴于没有答案,我决定自己测试一下。

就我所观察到的答案是,只要您更新不同的属性,它最终都会成功。我推送到项目的更新越多,确实需要更长的时间,因此它们似乎是按顺序而不是并行写入的。

我还尝试并行更新单个 List 属性,但预计会失败,一旦所有查询完成,结果列表就会被破坏,并且只有一些条目被推送到它。

我运行的测试非常基本,我可能会遗漏一些东西,但我相信结论是正确的。

为了完整起见,这是我使用的脚本 nodejs。

const aws = require('aws-sdk');
const ddb = new aws.DynamoDB.DocumentClient();

const key = process.argv[2];
const num = process.argv[3];


run().then(() => {
    console.log('Done');
});

async function run() {
    const p = [];
    for (let i = 0; i < num; i++) {
        p.push(ddb.update({
            TableName: 'concurrency-test',
            Key: {x: key},
            UpdateExpression: 'SET #k = :v',
            ExpressionAttributeValues: {
                ':v': `test-${i}`
            },
            ExpressionAttributeNames: {
                '#k': `k${i}`
            }
        }).promise());
    }

    await Promise.all(p);

    const response = await ddb.get({TableName: 'concurrency-test', Key: {x: key}}).promise();
    const item = response.Item;

    console.log('keys', Object.keys(item).length);
}
Run Code Online (Sandbox Code Playgroud)

像这样运行:

node index.js {key} {number}
node index.js myKey 10
Run Code Online (Sandbox Code Playgroud)

时间:

  • 10 次更新:~1.5s
  • 100 次更新:~2s
  • 1000 次更新:~10-20s(波动很大)

值得注意的是,指标显示了许多受限制的事件,但这些事件由 nodejs sdk 使用指数退避在内部处理,因此一旦尘埃落定,一切都按预期编写。

  • 在提出我的问题后,我找到了 DynamoDB 开发人员的官方演讲 (https://www.youtube.com/watch?v=yvBR71D0nAQ),解释了 DynamoDB 内部的工作原理。它解释了 DynamoDB 使用“领导者模型”,这意味着单个节点对同一项目进行所有更新。因此它可以轻松地序列化对同一项目的更新,因此对不同属性的并发更新能够存在也就不足为奇了。但我不明白为什么在这样的实现中,并发列表追加不起作用。你是怎么做到的? (5认同)

Ond*_*žka 6

你的帖子里有很多问题。

DynamoDB 的手册中有一条注释:

所有写入请求均按照收到的顺序应用。

我假设客户端按照通过调用传递的顺序发送请求。

这应该可以解决是否有任何保证的问题。如果您在多个仅更新这些属性的请求中更新某个项目的不同属性,那么它最终应该处于预期状态(不同更改的“总和”)。

另一方面,如果您更新整个对象,则最后一个将获胜。

DynamoDB@DynamoDbVersion可用于乐观锁定来管理整个对象的并发写入。

对于拍卖、并行滴答计数(例如“点赞”)等场景,DynamoDB 提供了AtomicCounters

如果您更新列表,这取决于您是否使用 DynamoDB 的列表类型 ( L),或者它是否只是一个属性并且客户端将列表转换为字符串 ( S)。因此,如果您读取一个属性,更改它,然后写入,并并行执行此操作,结果将受到最终一致性的影响 - 您将读取的内容可能不是最新写入的内容。应用于列表,并且多次应用,您最终会添加一些元素,而另一些则不添加(或者,更好地说,添加但然后被覆盖)。