如何运行带有锁定文档的MongoDB查询以供以后更新?

时间:2018-09-24 14:10:15

标签: mongodb mongodb-query

我正试图在MongoDB中找到这种情况的准确答案,但无法从任何来源获得令人满意的答案。

许多熟悉数据库培训示例的人,只是在万一有人需要的情况下进行解释。这是DB2中的示例。 Person-1的帐户余额为$ 100.00。 Person-1前往柜员/ ATM存入$ 20.00到他的帐户,余额更新在系统中执行以下步骤。为了更好地理解,我提供了活动时间。

  1. 在9:00:00:000 AM,读取Person-1的帐户记录以查找最新信息 余额(DB2锁定记录)。在DB2中读取等效于mongoDB 查询时,DB2具有记录级别的读取锁定机制。
  2. 在9:00:00:001 AM,对帐户进行一些检查并将$ 20加到100 = $ 120, 说这一步需要2秒钟(通常不会,只是为了更好 是时候了解更新机制了
  3. 在9:00:02:001,将$ 120更改为Person-1在DB2中的帐户记录。

Person-1的朋友在9:00:00:001在互联网上转移了30美元 如果系统此时读取Person-1的帐户以进行更新,则系统会将$ 30加到100,最终余额将变为$ 130或$ 120,这取决于以后发生的余额更新。但余额应为$ 150。

为避免该错误,DB2在步骤1的上方记录了更新锁定,因此Internet传输事务在步骤3完成之前无法读取余额以进行更新(步骤3自动释放锁定)。此外,只要其他线程不读取更新内容,其他线程就可以随时读取记录。

注意:在这种情况下,DB2仅锁定person-1的帐户记录,而不锁定整个表(集合),因为其他帐户更新可以同时继续。

有人可以解释一下如何在mongoDB中实现吗?我听到有人说在文档中使用标记并通过应用程序进行控制,但这是不准确的,在上述情况下也可能导致失去平衡。

谢谢 纳尔

1 个答案:

答案 0 :(得分:0)

  

有人可以解释一下在mongoDB中是如何实现的吗?

这里没有多少要打开的物品,但让我们从基本的MongoDB multi-document Transactions开始。

如果在事务会话内部执行更新操作,则第二个更新操作将被阻止,直到第一个事务已提交,中止或到期为止。在MDBW18: How and When to Use Multi-Document Distributed Transactions

上查看有关此行为的更多信息

例如(基于MongoDB v4.0.3),如果您具有以下文档:

{"person": "one", "balance": NumberDecimal(100)}

具有如下更新操作:

s1 = Mongo().startSession() ;
sessionFoo = s1.getDatabase("databaseName").collectionName;
s1.startTransaction();
var result = sessionFoo.update({"person":"one"}, {"$set":{"balance":120}});
// ...
s1.commitTransaction();

如果在commitTransaction之前发生了另一项更新操作(与上面类似),它将收到TransientTransactionError。错误输出示例:

"errorLabels" : [
    "TransientTransactionError"
],
"operationTime" : Timestamp(1540175676, 1),
"ok" : 0,
"errmsg" : "Unable to acquire lock '{7817449386782089629: Database, 899920359141007773}' within a max lock request timeout of '5ms' milliseconds.",
"code" : 24,
"codeName" : "LockTimeout",
  

在9:00:02:001,将$ 120更改为Person-1在DB2中的帐户记录

通常,您不会更新为某个值,而是对该值执行事件增量($inc operator)。因此,而不是:

var result = sessionFoo.update({"person":"one"}, {"$set":{"balance":120}});

应该是:

var result = sessionFoo.update({"person":"one"}, {"$inc":{"balance":20}});

要进一步扩展此功能(通常用于财务相关记录),您想捕获事件而不是状态。另请参见Even Sourcing。例如,而不是下面的示例:

t1: balance 100 
t2: balance 120
t3: balance 150 
t4: balance 70

其中balance代表时间t的平衡状态。可能是诸如以下事件:

t1: balance 100 
t2: balance +20 
t3: balance +30 
t4: balance -80

这样可以保存一个时间点的历史记录。即从48小时前开始重播交易。

  

在这种情况下,DB2仅锁定person-1的帐户记录,而不锁定整个表(集合),因为其他帐户更新可以同时继续

请注意,在MongoDB中,对单个文档的操作是原子的(即使没有事务功能)。这是因为您可以使用embedded文档和数组来捕获单个文档结构中数据之间的关系,而不是在多个文档和集合之间进行规范化,这种单文档原子性避免了很多实际使用中对多文档事务的需求案件。另请参见MongoDB Data Model Design