从MongoDB中的一个集合中选择100万用户的最快方法是什么?

时间:2016-11-21 04:32:03

标签: node.js mongodb

var i = 1,
    nodes = 8;

setInterval(function(){
  var rotation = i * 360 / nodes;
  i = i + 1;

  $('.cm').css({
    'transform': 'rotate(' + rotation + 'deg)'
  }).attr('data-rotation', rotation);

  $('.cm li').each(function (node){
    r = (node) * 360/nodes;
    $($('.cm li')[node]).find('span').css({
      'transform': 'rotate(' + ((rotation*-1) - r) + 'deg)'
    });
  });

  if(i >= nodes){
    i = 0;
  }
}, 1000);

正如我所说,我跑了

db.users.find({role:'seeker'}, {
    "email": 1,
    "name": 2
})

得到这个

db.users.find({role:'seeker'}).explain()

如果我使用上面的查询,则需要15-20秒才能响应。我需要每天向批量用户发送电子邮件广告系列。我有一个node.js任务来执行此操作,当此任务在后台运行时,即使node.js不应该阻塞,它也会减慢其他所有内容

2 个答案:

答案 0 :(得分:1)

我认为你做得对。就像select email, name from student where role='student'

一样简单

你有多少条记录?你有没有调整/ profile你的mongoDB实例?

你有role字段的索引吗? db.users.createIndex( { role: 1 } ) create-an-index-to-support-read-operations

如果您的查询是否使用索引,则可以使用explain获取一些信息。

  

对于无法使用索引的查询,MongoDB必须扫描所有文档   在与该查询匹配的文档的集合中。

如果每次都可以在您的应用中引入缓存,那么电子邮件列表是否相同?

答案 1 :(得分:0)

好的,所以在添加索引之后,解释看起来是正确的,并且确实使用了索引:

{ 
"queryPlanner" : { 
"plannerVersion" : 1, 
"namespace" : "users", 
"indexFilterSet" : false, 
"parsedQuery" : { 
"role" : { 
"$eq" : "seeker" 
} 
}, 
"winningPlan" : { 
"stage" : "FETCH", 
"inputStage" : { 
"stage" : "IXSCAN", 
"keyPattern" : { 
"role" : 1 
}, 
"indexName" : "role_1", 
"isMultiKey" : false, 
"direction" : "forward", 
"indexBounds" : { 
"role" : [ 
"[\"seeker\", \"seeker\"]" 
] 
} 
} 
}, 
"rejectedPlans" : [ ] 
}, 
"executionStats" : { 
"executionSuccess" : true, 
"nReturned" : 850834, 
"executionTimeMillis" : 13217, 
"totalKeysExamined" : 850834, 
"totalDocsExamined" : 850834, 
"executionStages" : { 
"stage" : "FETCH", 
"nReturned" : 850834, 
"executionTimeMillisEstimate" : 1970, 
"works" : 941141, 
"advanced" : 850834, 
"needTime" : 0, 
"needFetch" : 90306, 
"saveState" : 93946, 
"restoreState" : 93946, 
"isEOF" : 1, 
"invalidates" : 0, 
"docsExamined" : 850834, 
"alreadyHasObj" : 0, 
"inputStage" : { 
"stage" : "IXSCAN", 
"nReturned" : 850834, 
"executionTimeMillisEstimate" : 560, 
"works" : 850834, 
"advanced" : 850834, 
"needTime" : 0, 
"needFetch" : 0, 
"saveState" : 93946, 
"restoreState" : 93946, 
"isEOF" : 1, 
"invalidates" : 0, 
"keyPattern" : { 
"role" : 1 
}, 
"indexName" : "role_1", 
"isMultiKey" : false, 
"direction" : "forward", 
"indexBounds" : { 
"role" : [ 
"[\"seeker\", \"seeker\"]" 
] 
}, 
"keysExamined" : 850834, 
"dupsTested" : 0, 
"dupsDropped" : 0, 
"seenInvalidated" : 0, 
"matchTested" : 0 
} 
} 
}, 
"serverInfo" : { 
"host" : "xxx", 
"port" : 12345, 
"version" : "3.0.12", 
"gitVersion" : "33934938e0e95d534cebbaff656cde916b9c3573" 
}, 
"ok" : 1 
}

我认为通过“一次检索所有”可以获得最快的效果。 我想知道你是否可以retrieve only specific fields,例如'name'和'email':

  

如果未指定投影,则find()方法将返回所有字段   与查询匹配的所有文档。

     

db.inventory.find({type:'food'})此操作将全部返回   库存集合中的文档类型的值   田地是'食物'。返回的文档包含所有字段。

因此,如果您不希望mongo返回每个学生的所有字段,只需传递一些其他参数,如下所示:

db.users.find( { role: 'student' }, { name: 1, email: 1, _id:0 } )

显然,从硬盘中读取所有字段是一项耗时的操作,因此从10​​中仅检索2个字段将为您的查询提供显着的提升。就sql而言,它是:select * from Tselect name from T之间的区别。

我想知道您是否考虑使用分页和批处理获取/发送,即检索前10000名学生,发送电子邮件然后检索下一个10000名学生,发送电子邮件,重复...?

您还可以尝试通过在mongo数据库实例中对用户进行分片来进行水平扩展,但这取决于您的硬件功能,我不认为如果您只有一台服务器,它将为您带来任何好处。因此,分页应该是一种方法。