无法锁定 mongodb 文档。如果我需要怎么办?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11076272/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
It's not possible to lock a mongodb document. What if I need to?
提问by Mathieu Pagé
I know that I can't lock a single mongodb document, in fact there is no way to lock a collection either.
我知道我无法锁定单个 mongodb 文档,实际上也无法锁定集合。
However, I've got this scenario, where I think I need some way to prevent more than one thread (or process, it's not important) from modifying a document. Here's my scenario.
但是,我遇到了这种情况,我认为我需要某种方法来防止多个线程(或进程,这并不重要)修改文档。这是我的场景。
I have a collection that contains object of type A. I have some code that retrieve a document of type A, add an element in an array that is a property of the document (a.arr.add(new Thing()
) and then save back the document to mongodb. This code is parallel, multiple threads in my applications can do theses operations and for now there is no way to prevent to threads from doing theses operations in parallel on the same document. This is bad because one of the threads could overwrite the works of the other.
我有一个包含 A 类型对象的集合。我有一些代码可以检索 A 类型的文档,在作为文档 ( a.arr.add(new Thing()
)属性的数组中添加一个元素,然后将文档保存回 mongodb。这段代码是并行的,我的应用程序中的多个线程可以执行这些操作,目前没有办法阻止线程在同一文档上并行执行这些操作。这很糟糕,因为其中一个线程可能会覆盖另一个线程的工作。
I do use the repository pattern to abstract the access to the mongodb collection, so I only have CRUDs operations at my disposition.
我确实使用存储库模式来抽象对 mongodb 集合的访问,因此我只能处理 CRUD 操作。
Now that I think about it, maybe it's a limitation of the repository pattern and not a limitation of mongodb that is causing me troubles. Anyway, how can I make this code "thread safe"? I guess there's a well known solution to this problem, but being new to mongodb and the repository pattern, I don't immediately sees it.
现在我想起来,也许是存储库模式的限制而不是 mongodb 的限制给我带来了麻烦。无论如何,我怎样才能使这段代码“线程安全”?我想这个问题有一个众所周知的解决方案,但是作为 mongodb 和存储库模式的新手,我没有立即看到它。
Thanks
谢谢
采纳答案by Mahesh
Stumbled into this question while working on mongodb upgrades. Unlike at the time this question was asked, now mongodb supports document level locking out of the box.
在进行 mongodb 升级时偶然发现了这个问题。与问这个问题时不同,现在 mongodb 支持开箱即用的文档级锁定。
From: http://docs.mongodb.org/manual/faq/concurrency/
来自:http: //docs.mongodb.org/manual/faq/concurrency/
"How granular are locks in MongoDB?
“MongoDB 中的锁有多细?
Changed in version 3.0.
在 3.0 版本中更改。
Beginning with version 3.0, MongoDB ships with the WiredTiger storage engine, which uses optimistic concurrency control for most read and write operations. WiredTiger uses only intent locks at the global, database and collection levels. When the storage engine detects conflicts between two operations, one will incur a write conflict causing MongoDB to transparently retry that operation."
从 3.0 版开始,MongoDB 附带了 WiredTiger 存储引擎,该引擎对大多数读写操作使用乐观并发控制。WiredTiger 仅在全局、数据库和集合级别使用意图锁。当存储引擎检测到两个操作之间存在冲突时,就会引发写入冲突,导致 MongoDB 透明地重试该操作。”
回答by golja
Hey the only way of which I think now is to add an status parameter and use the operation findAndModify(), which enables you to atomically modify a document. It's a bit slower, but should do the trick.
嘿,我现在认为的唯一方法是添加一个状态参数并使用操作findAndModify(),它使您能够以原子方式修改文档。它有点慢,但应该可以解决问题。
So let's say you add an status attribut and when you retrieve the document change the status from "IDLE" to "PROCESSING". Then you update the document and save it back to the collection updating the status to "IDLE" again.
因此,假设您添加了一个状态属性,当您检索文档时,将状态从“空闲”更改为“正在处理”。然后更新文档并将其保存回集合,再次将状态更新为“空闲”。
Code example:
代码示例:
var doc = db.runCommand({
"findAndModify" : "COLLECTION_NAME",
"query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
"update" : {"$set" : {"status" : "RUNNING"} }
}).value
Change the COLLECTION_NAME and ID_DOCUMENT to a proper value. By default findAndModify() returns the old value, which means the status value will be still IDLE on the client side. So when you are done with updating just save/update everything again.
将 COLLECTION_NAME 和 ID_DOCUMENT 更改为适当的值。默认 findAndModify() 返回旧值,这意味着状态值在客户端仍然是 IDLE。因此,当您完成更新时,只需再次保存/更新所有内容。
The only think you need be be aware is that you can only modify one document at a time.
您唯一需要注意的是一次只能修改一个文档。
Hope it helps.
希望能帮助到你。
回答by Charlie Martin
"Doctor, it hurts when I do this"
“医生,我做这个会痛”
"Then don't dothat!"
“那你别这样!”
Basically, what you're describing sounds like you've got a serial dependency there -- MongoDB or whatever, your algorithm has a point at which the operation has to be serialized. That will be an inherent bottleneck, and if you absolutely must do it, you'll have to arrange some kind of semaphore to protect it.
基本上,您所描述的内容听起来像是您在那里有一个串行依赖项——MongoDB 或其他什么,您的算法有一个必须对操作进行序列化的点。这将是一个固有的瓶颈,如果您绝对必须这样做,则必须安排某种信号量来保护它。
So, the place to look is at your algorithm. Can you eliminate that? Could you, for example, handle it with some kind of conflict resolution, like "get record into local' update; store record" so that after the store the new record would be the one gotten on that key?
所以,要看的地方是你的算法。你能消除它吗?例如,您能否通过某种冲突解决方案来处理它,例如“将记录放入本地更新;存储记录”,以便在存储之后新记录将成为该键上的记录?
回答by Yaroslav Stavnichiy
Classic solution when you want to make something thread-safe is to use locks (mutexes). This is also called pessimistic lockingas opposed to optimistic lockingdescribed here.
当您想要使某些东西线程安全时,经典的解决方案是使用锁(互斥锁)。这也称为悲观锁定,而不是这里描述的乐观锁定。
There are scenarios when pessimistic locking is more efficient (more details here). It is also far easier to implement (major difficulty of optimistic locking is recovery from collision).
有些情况下悲观锁定更有效(更多细节在这里)。它也更容易实现(乐观锁定的主要困难是从冲突中恢复)。
MongoDB does not provide mechanism for a lock. But this can be easily implemented at application level (i.e. in your code):
MongoDB 不提供锁机制。但这可以在应用程序级别(即在您的代码中)轻松实现:
- Acquire lock
- Read document
- Modify document
- Write document
- Release lock
- 获取锁
- 阅读文档
- 修改文件
- 写文件
- 解除锁定
The granularity of the lock can be different: global, collection-specific, record/document-specific. The more specific the lock the less its performance penalty.
锁的粒度可以不同:全局、特定于集合、特定于记录/文档。锁越具体,其性能损失就越小。
回答by Mathieu Pagé
Answering my own question because I found a solution while doing research on the Internet.
回答我自己的问题,因为我在互联网上进行研究时找到了解决方案。
I think what I need to do is use an Optimistic Concurency Control.
我认为我需要做的是使用Optimistic Concurency Control。
It consist in adding a timestamp, a hash or another unique identifier (I'll used UUIDs) to every documents. The unique identifier must be modified each time the document is modified. before updating the document I'll do something like this (in pseudo-code) :
它包括向每个文档添加时间戳、散列或其他唯一标识符(我将使用 UUID)。每次修改文档时都必须修改唯一标识符。在更新文档之前,我会做这样的事情(在伪代码中):
var oldUUID = doc.uuid;
doc.uuid = new UUID();
BeginTransaction();
if (GetDocUUIDFromDatabase(doc.id) == oldUUID)
{
SaveToDatabase(doc);
Commit();
}
else
{
// Document was modified in the DB since we read it. We can't save our changes.
RollBack();
throw new ConcurencyException();
}
回答by Satyam
Update:With MongoDB 3.2.2 using WiredTiger Storage implementation as default engine, MongoDB use default locking at document level.It was introduced in version 3.0 but made default in version 3.2.2. Therefore MongoDB now has document level locking.
更新:MongoDB 3.2.2 使用 WiredTiger 存储实现作为默认引擎,MongoDB 在文档级别使用默认锁定。它是在 3.0 版中引入的,但在 3.2.2 版中成为默认值。因此 MongoDB 现在具有文档级锁定。
回答by Prashant Bhate
An alternative is to do in place update
另一种方法是进行就地更新
for ex:
例如:
http://www.mongodb.org/display/DOCS/Updating#comment-41821928
http://www.mongodb.org/display/DOCS/Updating#comment-41821928
db.users.update( { level: "Sourcerer" }, { '$push' : { 'inventory' : 'magic wand'} }, false, true );
which will push 'magic wand' into all "Sourcerer" user's inventory array. Update to each document/user is atomic.
这会将“魔杖”推入所有“Sourcerer”用户的库存数组。对每个文档/用户的更新是原子的。
回答by Govind Rai
As of 4.0, MongoDB supports Transactionsfor replica sets. Support for sharded clusters will come in MongoDB 4.2. Using transactions, commit up DB updates will be aborted if a conflicting write occurs, solving your issue.
从 4.0 开始,MongoDB 支持副本集的事务。MongoDB 4.2 将支持分片集群。使用事务,如果发生写入冲突,提交数据库更新将被中止,从而解决您的问题。
Transactions are much more costly in terms of performance so don't use Transactions as an excuse for poor NoSQL schema design!
事务在性能方面的成本要高得多,所以不要使用事务作为糟糕的 NoSQL 模式设计的借口!
回答by oderfla
Instead of writing the question in another question, I try to answer this one: I wonder if this WiredTiger Storage will handle the problem I pointed out here: Limit inserts in mongodb
我没有在另一个问题中写这个问题,而是尝试回答这个问题:我想知道这个 WiredTiger Storage 是否会处理我在这里指出的问题: Limit inserts in mongodb