MongoDB中相当简单的基于时间的队列

时间:2011-12-01 15:32:53

标签: mongodb nosql

我正在尝试使用MongoDB实现一个相当简单的队列。我有一个集合,许多愚蠢的工人需要处理。每个工作人员都应该搜索集合中的未处理工作,然后执行它。

我决定哪些作品未经处理的方式基于简单的计算。

基本上我有一组需要按特定时间间隔执行的作业,其中间隔作为interval存储在每个文档中,工作人员将扫描集合以查找至少未更新的文档interval时间。

文档的示例(省略_id字段)是:

{
  updated: 0360,
  interval: 60,
  work: "an object representing the work"
}

我想要的是一个原子/阻塞查询(有多个工作者),它返回一批文件updated + interval < currentTime,其中currentTime是数据库服务器上的时间,并设置updated字段currentTime

换句话说:

  1. find:updated + interval&lt; currentTime的
  2. 返回一批这些,比如30
  3. set:updated = currentTime
  4. 非常感谢任何帮助!

1 个答案:

答案 0 :(得分:3)

由于MongoDB不支持事务,因此您无法安全地对一批项目进行悲观锁定,除非您有一个单独的文档 - 最后还有更多内容。

让我们从查询开始:你不能查询某事。比如MongoDB中的'where x + y < z'。相反,您必须在下一个截止日期使用字段,例如nextDue

{
  "nextDue": "420",
  "work": { ... }
}

现在每个工作人员都可以获取几个项目(注意:这是所有伪代码,而不是特定的编程语言):

var result = db.queue.find( { "nextDue": { $gt, startTime } }).limit(50);
// hint: you can do a random skip here to decrease the chances of collisions
// between workers.

foreach(rover in result)
{
    // pessimistic locking: '-1' indicates this is in progress. 
    // I'd recommend a flag instead, however...

    var currentItem = db.queue.findAndModify({ "_id" : rover.id, "nextDue" : {$gt, startTime}}, {$set : {"nextDue" : -1}});

    if(currentItem == null)
        continue; // hit a lock: another worker is processing this already

    // ... process job ...

    db.queue.findAndModify({ "_id" : rover.id, "nextDue" : "-1"}, {$set : {"nextDue" : yourNextDue }});
}

对于多个文档的悲观锁定,我看到基本上有两种方法。一种是为您要锁定的文档创建存储桶,将作业描述符放入存储桶并处理这些存储桶。从现在开始,桶就是单个对象,你可以依赖原子修饰符。

另一个是使用two-phase commit,它还为事务创建了另一个对象,但不要求您将文档移动到其他文档中。然而,这是一个有点复杂的模式。

上面介绍的伪代码在两个应用程序中运行良好,但在两个应用程序中,单个作业需要相当长的时间才能执行(半秒到几个小时)。