首页 > 解决方案 > MongoDB 部分索引无法按预期使用计数

问题描述

mongoDB 3.6.3上,我用两百万条记录创建了这个集合:

function randInt(n) { return parseInt(Math.random()*n); }

for(var j=0; j<20; j++) {  
  print("Building op "+j);
  var bulkop=db.media.initializeOrderedBulkOp() ;
  for (var i = 0; i < 100000; ++i) {
    bulkop.insert(    
      {
        id_profile: NumberLong("222"),
        needle_id: NumberInt(randInt(2000000000)),
        visibility: NumberInt(randInt(5)),
      }
    )
  };
  print("Executing op "+j);
  bulkop.execute();
}

然后我创建这个部分索引

db.media.createIndex( 
  {"id_profile": 1, "visibility": 1}, 
  {unique: false, partialFilterExpression: { "needle_id": { $exists: true } }} 
); 

然后我运行这个与部分索引完全匹配的查询:

db.media.count({$and:[
  {id_profile:NumberInt(222)},
  {visibility:NumberInt(0)},
  {needle_id:{$exists:true}}]}) 

但它很慢:(事实上,它的速度就像我没有使用部分索引一样,我需要过滤所有没有needle_id的文档:

db.media.createIndex( 
  {"id_profile": 1, "visibility": 1}, 
  {unique: false} 
); 

db.media.count({$and:[
  {id_profile:NumberInt(222)},
  {visibility:NumberInt(0)},
  {needle_id:{$exists:true}}]}) 

那么它是部分索引的错误吗?我能做些什么来加快我的计数?

标签: mongodbmongodb-querymongodb-indexes

解决方案


我调查这个问题已经有一段时间了,似乎对部分索引运行计数查询并不能避免遍历索引的所有文档。

在大型集合上创建索引时,运行基于索引的计数查询会在几毫秒内完成。它实际上是在计算所提供值的索引大小。

在查看此类查询的执行计划时,我们可以单独注意到“IDX_SCAN”阶段,它代表索引扫描。(扫描请求边界上的索引并计算它包含的文档数)

但是当使用部分索引时,mongo 实现的计数功能似乎不是在进行索引扫描,而是在文档上运行,就像我们运行修改查询一样。然后最终返回文档的总数。

这可以通过查看此类查询的执行计划来证明,并看到它显示阶段是“FETCH”,而不是“IDX_SCAN”,而是使用索引获取,所以如果索引不是在全部。

更完整的信息可以在这里找到:为什么“distinct”和“count”命令在 MongoDB 中的索引项上发生如此缓慢?


推荐阅读