我正在尝试使用MongoDB实现一个相当简单的队列。我有一个集合,许多愚蠢的工人需要处理。每个工作人员都应该搜索集合中的未处理工作,然后执行它。
我决定哪些作品未经处理的方式基于简单的计算。
基本上我有一组需要按特定时间间隔执行的作业,其中间隔作为interval
存储在每个文档中,工作人员将扫描集合以查找至少未更新的文档interval
时间。
文档的示例(省略_id
字段)是:
{
updated: 0360,
interval: 60,
work: "an object representing the work"
}
我想要的是一个原子/阻塞查询(有多个工作者),它返回一批文件updated + interval < currentTime
,其中currentTime
是数据库服务器上的时间,并设置updated
字段currentTime
。
换句话说:
非常感谢任何帮助!
答案 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,它还为事务创建了另一个对象,但不要求您将文档移动到其他文档中。然而,这是一个有点复杂的模式。
上面介绍的伪代码在两个应用程序中运行良好,但在两个应用程序中,单个作业需要相当长的时间才能执行(半秒到几个小时)。