首页 > 解决方案 > 从包含约 120 万条记录的集合中,Mongo 查找查询失败

问题描述

有 2 个集合:AlertsAlertTypesAlerts集合有一个名为:alertTypeId的字段,它是AlertTypes集合的查找/外键。

我需要优化以下查询,通过加入相应的集合从Alerts集合中获取数据以及 AlertType 名称。

我使用聚合函数如下:

db.Alerts.aggregate([{
    "$match": {
        "status": {
            "$ne": -1
        },
        "type": 4
    }
}, {
    "$lookup": {
        "localField": "alertTypeId",
        "from": "AlertTypes",
        "foreignField": "_id",
        "as": "alertTypeRel"
    }
}, {
    "$project": {
        "title": 1,
        "type": 1,
        "alertTypeId": 1,
        "alertTypeRel.alertTypeName": 1,
        "priority": 1,
        "message": 1,
        "status": 1,
        "startDate": 1,
        "createdAt": 1,
        "createdBy": 1,
        "validUntil": 1,
        "errorFlag": 1,
        "extApiId": 1,
        "errorMessage": 1,
        "autoPublish": 1,
        "statusChangedBy": 1
    }
},{
    "$sort": {
        "status": 1,
        "createdAt": -1
    }
}, {
    "$group": {
        "_id": null,
        "count": {
            "$sum": 1
        },
        "results": {
            "$push": "$$ROOT"
        }
    }
}, {
    "$project": {
        "total": "$count",
        "_id": 0,
        "results": {
            "$slice": ["$results", 0, 10]
        }
    }
}], {
    "collation": {
        "locale": "en",
        "strength": 2
    },
    "allowDiskUse": true,
    "cursor": {}
}).pretty();

我也索引了这些字段。例如:

{
        "v" : 2,
        "key" : {
            "status" : 1,
            "createdAt" : -1
        },
        "name" : "status_1_createdAt_-1"
}

AlertsAlertTypes集合中分别有 1 250 543 和 117 条记录。我也尝试了facet查询,但也抛出了以下相同的结果:

uncaught exception: Error: command failed: {
    "ok" : 0,
    "errmsg" : "$push used too much memory and cannot spill to disk. Memory limit: 104857600 bytes",
    "code" : 146,
    "codeName" : "ExceededMemoryLimit"
} : aggregate failed :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
doassert@src/mongo/shell/assert.js:18:14
_assertCommandWorked@src/mongo/shell/assert.js:639:17
assert.commandWorked@src/mongo/shell/assert.js:729:16
DB.prototype._runAggregate@src/mongo/shell/db.js:266:5
DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1058:12
@(shell):1:1

谢谢

标签: mongodbaggregation-frameworklookup

解决方案


阶段在$group推送对象时使用了太多内存$$ROOT,修复很少,只需使用和阶段$facet代替,$group$project

  • 对于您可以使用的分页$skip$limit阶段,
  • 对于计数总文档使用$count运算符
  • $facet将两者分开result并且count
  • 使用$lookupafter $limitstage 因为我们要获取 10 个文档,并且只需要查找 10 个文档
  • $project如果需要,在查找后使用

你最后的查询是,

db.Alerts.aggregate([
  {
    "$match": {
      "status": { "$ne": -1 },
      "type": 4
    }
  },    
  {
    "$sort": {
      "status": 1,
      "createdAt": -1
    }
  },
  {
    $facet: {
      result: [
        { $skip: 0 },
        { $limit: 10 },
        {
          "$lookup": {
           "localField": "alertTypeId",
           "from": "AlertTypes",
           "foreignField": "_id",
           "as": "alertTypeRel"
          }
        },
        {
          "$project": {
            "title": 1,
            "type": 1,
            "alertTypeId": 1,
            "alertTypeRel.alertTypeName": 1,
            "priority": 1,
            "message": 1,
            "status": 1,
            "startDate": 1,
            "createdAt": 1,
            "createdBy": 1,
            "validUntil": 1,
            "errorFlag": 1,
            "extApiId": 1,
            "errorMessage": 1,
            "autoPublish": 1,
            "statusChangedBy": 1
          }
        }
      ],
      count: [{ $count: "total" }]
    }
  } 
], 
{
  "collation": {
    "locale": "en",
    "strength": 2
  },
  "allowDiskUse": true,
  "cursor": {}
})
.pretty();
  • 为了获得更高的性能,您可以在匹配条件字段和排序字段上使用索引,根据您的查询使用可以使用复合索引statustypecreatedAt查看有关复合索引的更多详细信息

此查询未经测试!


推荐阅读