一种确保MongoDb的findAndModify独占读取的方法?

时间:2013-03-19 16:31:46

标签: mongodb pymongo

我有一个MongoDB集合(用作作业队列),多个进程使用findAndModify从中读取记录。 FindAndModify搜索active字段为“false”的记录,将其设置为“true”,以便其他进程不会读取相同的记录。

问题在于查看日志,我发现不同的进程仍然读取相同的记录。当两个进程同时从队列中读取时,似乎会发生这种情况。有没有办法确保一个唯一的进程一次从一个集合中读取?

我正在使用Mongo 2.2.3和pymongo 2.2。

非常感谢!

编辑:有问题的日志是:

  

worker.3 2013-03-18 23:57:45,434 default-worker-3
  project_name INFO队列作业:作业ID:5147a90f68e8fe0097002bdf

     

worker.3 2013-03-18 23:57:47,608 default-worker-3
  project_name INFO输入:14497 docs

     

worker.2 2013-03-18 23:57:45,440 default-worker-2
  project_name INFO队列作业:作业ID:5147a90f68e8fe0097002bdf

     

worker.2 2013-03-18 23:57:47,658 default-worker-2
  project_name INFO输入:14497 docs

如您所见,worker.3和worker.2从队列中读取相同的作业(它对两个工作者都具有相同的mongodb id)。

find_and_modify命令:

query = {"active": False}
try:
    return self.collection.find_and_modify(
            query=query,
            update={"$set": {"active": True}},
            upsert=False,
            sort={"added_on": 1},
            limit=1
        )
except Exception, exc:
    LOGGER.exception(exc)

3 个答案:

答案 0 :(得分:4)

让我说清楚一点 - 两个不同的findAndModify命令不可能在你的场景中返回相同的文档。

不可能。以下是执行工作的方法的前几行:

    Lock::DBWrite lk( ns );
    Client::Context cx( ns );

    BSONObj doc;

    bool found = Helpers::findOne( ns.c_str() , queryOriginal , doc );

注意第122行,其中在查找之前获取了WRITE锁。

https://github.com/mongodb/mongo/blob/master/src/mongo/db/commands/find_and_modify.cpp#L122

两个进程同时保持写锁定是。似乎更有可能发生了不同的事情(多个文件具有相同的id值,同一文档由调用find_and_modify的函数返回,将其返回到两个线程,其他我们不知道的东西可以推测)。

FindAndModify是一个原子命令,在执行期间保存独占写锁。我的建议是深入了解日志实际显示的内容,而不是基于对必须进行的内容的错误/无根据的假设来更改代码。

答案 1 :(得分:1)

将您的“锁定”分为两个阶段。第一次更新记录查询没有锁定时间戳或具有过期时间戳并设置新锁定的第一个对象。然后使用您刚刚建立的锁定数据找到相同的对象。

答案 2 :(得分:1)

首先向mongodb进行简单的查询以获得单个作业记录,

job = db.coll.find({query}).limit(1)

接下来更新指定作业ID的记录以及active=false

的位置
update_response = db.coll.update(
    {_id:job.id, active=false},
    {$set:{active:true}},
    false,
    false
)

如果作业已被其他进程更新,则由于active=false的查询约束,更新将不会成功。检查update_response是否更新了记录:

if update_response['n'] > 0 and update_response['updatedExisting']==true:
    return job

如果您的更新未成功,请获取其他作业并重试。