Spring数据MongoDB并发更新行为

Spring数据MongoDB并发更新行为,mongodb,concurrency,mongodb-java,Mongodb,Concurrency,Mongodb Java,假设有一个文档包含一个字段:{availableSpots:100} 还有数以百万计的用户,通过向API服务器发送请求,竞相抢占一席之地 每次请求到来时,服务器都会读取文档,如果availableSpot大于0,则会将其递减1,并在另一个集合中创建预订 现在我了解到每当执行更新操作时,mongodb都会锁定文档 如果有一百万个并发请求,会发生什么?是否会因为同一文档一直被锁定而花费很长时间?此外,服务器在尝试更新文档之前读取文档的值,当它获取锁时,spot可能不再可用 线程在同一时刻获得“ava

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

还有数以百万计的用户,通过向API服务器发送请求,竞相抢占一席之地

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

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

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


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

MongoDB使用Wired Tiger作为启动3.2版的默认存储引擎

Wired Tiger提供:

从文档:

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

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


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

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

1。原子性

如果AvailableSpot>0,则更新(减少1)的操作:

db.collection.updateOne({"availableSpots" :{$gt : 0}}, { $inc: { availableSpots: -1 })
它是原子的

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

参考:

2。并发性 因为MongoDB对写操作具有文档级并发控制。每次更新都会锁定文档

现在请回答您的问题:

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

是的,每个更新都将逐个执行(由于锁定),因此会减慢速度

服务器在尝试更新文档之前读取文档的值 文档,并且在获取锁时,该点可能不会被锁定 现在没有了


因为操作是原子的,所以不会发生这种情况。它将根据您的需要工作,只会执行100个更新,受影响的行数大于0或等于1。

我的理解是,您关心的是针对两个独立集合的许多并发ACID兼容事务的性能:

  • 一个集合(我们称之为
    spots
    ),包含一个文档{availableSpots:999..}
  • 另一个集合(我们称之为
    预订
    )包含多个文档,每个预订一个文档。 现在我了解到每当执行更新操作时,mongodb都会锁定文档
线程也可能获得“availableSpot>0” 在同一时刻是正确的,但实际上是有效的 可能不足以满足所有请求。如何应对

在版本4.0中,MongoDB提供了对副本集执行多文档事务的能力。(即将发布的MongoDB 4.2将把这种多文档ACID事务功能扩展到分片集群。)

这意味着在事务提交之前,多文档事务中的任何写入操作(例如根据您建议的方法对
预订
集合进行更新)都不会在事务外部可见

尽管如此,正如本文所述,非规范化方法通常比多文档事务提供更好的性能:

在大多数情况下,多文档事务会带来更高的性能 单个文档写入的成本,以及 多文档交易不应取代有效的 模式设计。对于许多场景,非规范化数据模型 (嵌入式文档和数组)将继续是您的最佳选择 数据和用例。也就是说,对于许多场景,建模数据 适当地将多文档事务的需求降至最低

在MongoDB中,对单个文档的操作是原子的。因为您可以使用嵌入式文档和数组来捕获单个文档结构中的数据之间的关系,而不是跨多个文档和集合进行规范化,所以这种单文档原子性消除了许多实际用例对多文档事务的需要

但请记住,如果在一个集合中作为一个非规范化文档(包含一个可用的spot子文档和数千个
预订
子文档)实现您的用例,则可能不可行,因为最大文档大小为16MB


因此,总而言之,编写原子性的非规范化方法通常比多文档方法性能更好,但受最大文档大小16MB的限制。

您可以在尝试更新文档时尝试使用
findAndModify()
选项。每次您都需要在特定文档中选择要更新的字段。此外,由于mongo db将数据复制到主节点和辅助节点,因此您可能还需要调整
WriteConcern
值。您可以在官方文档中阅读更多关于此的信息。我使用spring
mongoTemplate在
mongoDB
中处理类似类型的并发问题。如果您想要任何与java相关的参考资料,请告诉我