Spring Data MongoDB 并发更新行为

use*_*934 5 concurrency mongodb mongodb-java

假设有一个包含单个字段的文档:{availableSpots: 100}

数以百万计的用户竞相通过向 API 服务器发送请求来获得席位。

每次收到请求时,服务器都会读取文档,如果 availableSpot > 0,则会将其减 1 并在另一个集合中创建预订。

现在我读到每当执行更新操作时 mongodb 都会锁定文档。

如果有一百万个并发请求会发生什么?是否会因为同一个文档不断被锁定而需要很长时间?此外,服务器在尝试更新文档之前会读取文档的值,并且当它获取锁时,该位置可能不再可用。

线程也有可能在同一时刻获得“availableSpot > 0”为真,但实际上,availableSpot 可能不足以满足所有请求。这该如何处理呢?

Raj*_*oel 5

这里最重要的是原子性和并发性。

1. 原子性

如果 availableSpots > 0 则更新操作(减一):

db.collection.updateOne({"availableSpots" :{$gt : 0}}, { $inc: { availableSpots: -1 })
Run Code Online (Sandbox Code Playgroud)

是原子的。

$inc 是单个文档中的原子操作。

参考: https: //docs.mongodb.com/manual/reference/operator/update/inc/

2.并发性 由于MongoDB对写操作有文档级的并发控制。每次更新都会锁定文档。

现在你的问题:

如果有一百万个并发请求会发生什么?

是的,每次更新都将一一执行(由于锁定),因此速度会变慢。

服务器在尝试更新文档之前读取文档的值,并且当它获取锁时,该位置可能不再可用。

由于操作是原子的,因此不会发生这种情况。它将按照您的意愿工作,仅执行 100 次更新,且受影响的行数大于 0 或等于 1。


Ati*_*ish 3

从 3.2 版本开始,MongoDB 使用 Wired Tiger 作为默认存储引擎。

Wired Tiger 提供文档级并发

来自文档:

WiredTiger 对写操作使用文档级并发控制。因此,多个客户端可以同时修改集合中的不同文档。

对于大多数读写操作,WiredTiger 使用乐观并发控制。WiredTiger 仅在全局、数据库和集合级别使用意向锁。当存储引擎检测到两个操作之间存在冲突时,其中一个操作将引发写入冲突,导致 MongoDB 透明地重试该操作。

当多个客户端尝试更新文档中的值时,只有该文档将被锁定,而不是整个集合。