在Node.js中防止与数据库相关的竞争条件

Nic*_* S. 20 mongoose mongodb node.js

概观

我试图了解如何在使用Node.js时使用模型实例时确保同步安全.这里,我在代码示例中使用Mongoose ODM,但问题适用于数据库与Node.js采用的异步事件驱动I/O方法一起使用的任何情况.

请考虑以下代码(使用Mongoose进行MongoDB查询):

片段A.

MyModel.findOne( { _id : <id #1> }, function( err, doc ) {
    MyOtherModel.findOne( { _id : someOtherId }, ( function(err, otherDoc ) {
        if (doc.field1 === otherDoc.otherField) {
            doc.field2 = 0; // assign some new value to a field on the model
        }
        doc.save( function() { console.log( 'success' ); }
    });
});
Run Code Online (Sandbox Code Playgroud)

在应用程序的单独部分中,可以更新MyModel描述的文档.请考虑以下代码:

代码片段B.

MyModel.update( { _id : <id #1> }, { $set : { field1 : someValue }, callback );
Run Code Online (Sandbox Code Playgroud)

在Snippet A中,发出MongoDB查询,并在文档准备好后触发注册的回调.MyModel描述的文档实例保留在内存中(在"doc"对象中).可能会发生以下顺序:

  1. 代码片段A执行
  2. 为MyModel启动一个查询,注册一个回调(回调A)供以后使用
  3. <<节点事件循环运行>>
  4. 从数据库中检索MyModel,执行已注册的回调(回调A)
  5. 为MyOtherModel启动一个查询,注册一个回调供以后使用(回调B)
  6. <<节点事件循环运行>>
  7. 代码片段B执行
  8. 文档(id#1)已更新
  9. <<节点事件循环运行>>
  10. 从数据库中检索MyOtherModel,执行已注册的回调(回调B)
  11. 在比较中错误地使用了文档的陈旧版本(id#1).

问题

  1. 是否有任何保证在Node.js/MongoDB中不会发生这种竞争条件?
  2. 我该怎么做才能确定地防止这种情况发生?

虽然Node以单线程方式运行代码,但在我看来,任何允许运行的事件循环都会为可能过时的数据打开大门.如果这种遵守是错误的,请纠正我.

Joh*_*yHK 15

不,不能保证node.js/MongoDB中不会出现这种类型的竞争条件.它与node.js没有任何关系,这对任何支持并发访问的数据库都是可能的,而不仅仅是MongoDB.

但是,使用MongoDB解决问题比较棘手,因为它不支持像典型SQL数据库那样的事务.所以,你必须使用像MongoDB的菜谱列出的其中一个策略来解决它在应用程序层这里.