插入文档时的mongodb并发性

时间:2017-04-10 06:58:32

标签: mongodb concurrency

我们目前有两个集合:TeachersStudents,我们限制每位教师只能拥有 30 学生,当然我已经设置了参考密钥比如每个文档上的studentsIdteachersId

当我即将创建一名新学生时,如何确保我只在指定教师的学生人数少于30人时才创建?

// get students count under specify teacher
const StudentNums = await Teachers.findById(1234).studentsId.length;
if (StudentNums < 30) /* create student */

(上面的代码写的是JS)

毫无疑问,上面的代码会在并发时失败,导致mongodb共享读锁,任何想法?事先提前。

2 个答案:

答案 0 :(得分:1)

您可以在应用程序级别应用标准Optimistic lock

假设教师收藏中的文件有以下字段:

db.teachers.insert({
    _id: 1234,
    students: [12,34],
    version: 8
});

其中_idstudents是对象的有效负载,而version是用于并发控制的内部字段。问题中的更新可能如下所示:

var teacher = db.teachers.findById(1234);

if(teacher.students.length < 30) {
    var res = db.teachers.update({_id: 1234, version: teacher.version}, {$push: {students: 56}, $inc: {version: 1}});
}

if(res.nModified < 1) {
    // apply retry logic
}

如果发生并发更新,则数据库中文档的版本不会与更新的查询部分匹配,这将导致更新0个文档。

答案 1 :(得分:1)

为什么不检查您的老师是否可以容纳更多学生,而不是处理与并发相关的任何事情?您可以在一次操作中检查和更新,以确保原子性。

它基本上归结为在查找查询中添加学生长度检查,如下所示:

teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}

在下面添加了一个测试用例:

 const mongodb = require('mongodb')
 const mongoClient = mongodb.MongoClient

 mongoClient.connect('mongodb://localhost:27017/stackoverflow', function (err, db) {
   if (err) {
     console.log(`DB connect error ${JSON.stringify(err)}`)
   } else {
     const teachers = db.collection('teachers')
     let doc = {id: 1, name: 'Ms Anderson', students: [1, 2, 3, 4, 5, 6, 7], subject: 'Matrix'}
     teachers.insert(doc, {w: 1}, function (err, result) {
       console.log(`insert error ${JSON.stringify(err)}`)
       console.log(`insert result : ${JSON.stringify(result)}`)
//Check the $where parameter, it will return only if student array has size less than 8
       teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}, {w: 1}, function (err, result) {
         console.log(`update error ${JSON.stringify(err)}`)
         console.log(`update result : ${JSON.stringify(result)}`)
       })
     })
   }
 })
如果文档更新,则

result为1,否则为0。