Node.js 如何标记多个MongoDB文档进行处理?
我正在实现一个用Node编写的web爬虫,并使用MongoDB作为应用程序的后端来存储页面及其状态。爬虫程序应该能够在多台机器上运行,除此之外,每台机器将有多个并行运行的工作程序,以加快挂起页面的爬网过程 每位工人将:Node.js 如何标记多个MongoDB文档进行处理?,node.js,mongodb,Node.js,Mongodb,我正在实现一个用Node编写的web爬虫,并使用MongoDB作为应用程序的后端来存储页面及其状态。爬虫程序应该能够在多台机器上运行,除此之外,每台机器将有多个并行运行的工作程序,以加快挂起页面的爬网过程 每位工人将: 在数据库中查询仍有待爬网的部分页面 将其状态从“待定”更新为“正在进行” 爬行 将其状态从“进行中”更新为“完成” 考虑到这一点,我正试图找到让多个工作人员不同时查询相同页面的方法 每个工作人员都有其唯一的ID,因此页面只是具有如下结构的文档: { uri, status, wo
{ uri, status, workerId, <other data> }
{uri,status,workerId,}
我的计划是用当前工作人员id标记N
文档(通知它们将由该工作人员处理),然后查询它们
对于具有以下状态的文档,例如将workerId设置为
:{“status”:“Pending”,“workerId”:null}
然后查询具有以下内容的文档:{“status”:“Pending”,“workerId”:“}
问题是,据我所知,mongo不支持有限制的更新。当然,我可以执行更新单个文档的N
update操作,但我想知道是否有一种更为惯用/优雅的解决方案用于此类任务
最后,我的目标是确保每当2个或更多工作人员查询要处理的页面时,他们不会检索同一页面两次。好吧,我想我理解这个目标-您希望更新所有处于挂起状态的文档并为其分配工作人员。你想把工人分配得稍微均匀一些。工作分配完成后,每个工作人员将识别要扫描的页面。但是,您不喜欢一次将光标移动到一个文档中,而是希望一次更新一组数据 下面是一个在updateMany()函数中使用$where条件的示例。请记住$where不能使用索引。如果您在“状态”上建立索引,您可能还可以,但从性能角度来看,这可能不起作用。我相信您希望更新所有挂起的记录,因此与一次更新一条记录相比,这种方式对性能的影响可能更好。此外,我的查询谓词不考虑WorkID是否为NULL。这是因为我相信永远不应该存在状态为“待定”且workerId不为null的情况 假设有两个worker,我的想法实现了两个update语句,一个用于worker0,另一个用于worker1。我假设您的文档有一个名为_id的字段,它是ObjectId。策略是使用_id字段时间戳。看看时间戳的秒数。对于秒数值介于0和30之间的用户,将其分配给worker0,其他所有用户将其分配给worker1。如果您有更多的员工,则需要更改此策略以适应所需员工的数量 工人0分配:
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 0 && seconds < 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 0} })
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 1} })
db.pages.find({status: "In Progress", workerId: 0})
一旦运行这些查询,分配就完成了。现在,每个工作人员都可以通过发出各自的查询来识别要爬网的页面。例如:
Worker0识别要爬网的页面:
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 0 && seconds < 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 0} })
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 1} })
db.pages.find({status: "In Progress", workerId: 0})
工人0已完成:
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 0 && seconds < 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 0} })
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 1} })
db.pages.find({status: "In Progress", workerId: 0})
一旦工作程序对页面进行爬网,它可以标记记录已完成,以防止将来的多次爬网运行
db.pages.updateOne({_id: ObjectId("5db0b1953cf0c979dd020fa2")}, { $set: {status: "Finished"}})
结论:
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 0 && seconds < 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 0} })
db.pages.updateMany({"status": "Pending", $where: function(){
var seconds = this._id.getTimestamp().getSeconds()
if(seconds >= 30) {
return true;
}
else {
return false;
}
}
}, { $set: { status: "In Progress", workerId: 1} })
db.pages.find({status: "In Progress", workerId: 0})
我很好奇你对这种方法的想法,并感谢任何反馈,无论是好是坏。点火
思考之后
一种完全不同的方法可能是,在最初使用随机分配插入记录时分配工作者。但是,这对已经使用空分配创建的记录没有帮助。没有创建单独的调度程序进程来分配工作,可能是三阶段方法
var ids = db.pages.find({status:"pending", workerId: null},{_id:1}).limit(100).toArray().map(p=>p._id)
db.pages.updateMany({_id:{$in:ids}},{$set:{status:"In Progress", worker: MyID}})
var workcursor = db.pages.find({status:"In Progress", worker: MyID})
如果您有多个员工同时进入,则可能会发生竞争,他们可能都试图获得相同的页面。您可以在a中执行上述步骤以避免出现这种情况。您运行的是哪个版本的MongoDB,您是否进行了切分?这不只是一个关于使用查询更新多个文档的问题吗@RobertMoskal-是的,但我认为更具体地说,如何构造update语句的find部分,以便选择记录是伪随机的,以便均匀分布。