锁定mongodb文档是不可能的.如果需要怎么办?

Mat*_*agé 29 repository-pattern mongodb

我知道我无法锁定单个mongodb文档,实际上也无法锁定集合.

但是,我有这种情况,我认为我需要一些方法来防止多个线程(或进程,这并不重要)修改文档.这是我的情景.

我有一个包含类型A的对象的集合.我有一些代码检索类型A的文档,在数组中添加元素作为document(a.arr.add(new Thing())的属性,然后将文档保存回mongodb.这段代码是并行的,我的应用程序中的多个线程可以执行这些操作,现在没有办法阻止线程在同一文档上并行执行这些操作.这很糟糕,因为其中一个线程可能会覆盖另一个线程的工作.

我确实使用存储库模式来抽象对mongodb集合的访问,所以我只处理我的处理中的CRUD操作.

现在我考虑一下,也许这是对存储库模式的限制,而不是mongodb的限制导致我的麻烦.无论如何,我怎样才能使这段代码"线程安全"?我想这个问题有一个众所周知的解决方案,但是对mongodb和存储库模式不熟悉,我不会马上看到它.

谢谢

小智 14

嘿,我现在认为唯一的方法是添加一个状态参数并使用操作findAndModify(),这使您能够原子地修改文档.它有点慢,但应该做的伎俩.

因此,假设您添加了一个状态attribut,当您检索文档时,将状态从"IDLE"更改为"PROCESSING".然后,您更新文档并将其保存回集合,再次将状态更新为"IDLE".

代码示例:

var doc = db.runCommand({
              "findAndModify" : "COLLECTION_NAME",
              "query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
              "update" : {"$set" : {"status" : "RUNNING"} }
}).value
Run Code Online (Sandbox Code Playgroud)

将COLLECTION_NAME和ID_DOCUMENT更改为正确的值.默认情况下,findAndModify()返回旧值,这意味着客户端的状态值仍然是IDLE.因此,当您完成更新后,只需再次保存/更新所有内容.

您需要注意的唯一想法是您一次只能修改一个文档.

希望能帮助到你.


Mah*_*esh 7

在进行mongodb升级时偶然发现了这个问题.与此问题不同,现在mongodb支持开箱即用的文档级锁定.

来自:http://docs.mongodb.org/manual/faq/concurrency/

"MongoDB中的锁是多么精细?

在3.0版中更改.

从版本3.0开始,MongoDB附带了WiredTiger存储引擎,该引擎对大多数读写操作使用乐观并发控制.WiredTiger仅在全局,数据库和集合级别使用意图锁.当存储引擎检测到两个操作之间的冲突时,会发生写入冲突,导致MongoDB透明地重试该操作."

  • 如果我想在创建文档对象期间锁定该怎么办? (3认同)

Cha*_*tin 6

"医生,这很伤我这样做时,这个 "

"那就不要那样了!"

基本上,你所描述的内容听起来就像你有一个串行依赖 - MongoDB或其他什么,你的算法有一个点,操作必须序列化.这将是一个固有的瓶颈,如果你绝对必须这样做,你将不得不安排某种信号量来保护它.

所以,要看的地方是你的算法.你能消除吗?例如,您是否可以通过某种冲突解决方案处理它,例如"将记录记录到本地'更新;存储记录",以便在存储之后新记录将是获得该密钥的记录?

  • 我查理,谢谢回答。我不明白您提出的冲突解决方案。我同意我确实需要更改算法,并且可以想象出一些解决方案,但是我认为必须对此问题达成一些商定的解决方案。在我看来,这是一个经典的问题,许多使用mongodb(或可能是任何数据库)的人都遇到了。如果是内存中更新,我将知道如何使用互斥锁“锁定”我要更新的变量,因此一次只有一个线程对其进行更新。我想我的问题是:其他程序员通常如何处理这种情况? (2认同)
  • 很棒的评论。即使这是你必须做的工作,也不要因为某些工具不够好而去做。 (2认同)
  • **MongoDB 终于支持事务** :D /sf/answers/3766003391/ (2认同)

Yar*_*hiy 6

当您想要使某些东西线程安全时,经典的解决方案是使用锁(互斥锁)。这也称为悲观锁定,而不是这里描述的乐观锁定

有些情况下悲观锁定更有效(更多细节在这里)。它也更容易实现(乐观锁定的主要困难是从冲突中恢复)。

MongoDB 不提供锁机制。但这可以在应用程序级别(即在您的代码中)轻松实现:

  1. 获取锁
  2. 阅读文档
  3. 修改文件
  4. 写文件
  5. 解除锁定

锁的粒度可以不同:全局、特定于集合、特定于记录/文档。锁越具体,其性能损失就越小。

  • 这在具有多个实例的应用程序中不起作用。 (20认同)