MongoDB 聚合管道的操作超时

Posted

技术标签:

【中文标题】MongoDB 聚合管道的操作超时【英文标题】:Operation timeout for a MongoDB aggregation pipeline 【发布时间】:2021-07-13 11:23:25 【问题描述】:

我在 MongoDB Atlas 上有一个 MongodDB 数据库。

它有“orders”、“products”、“itemTypes”和“brands”。

“orders”仅跟踪订购的产品 ID。 “products”只记录品牌 id 和 itemType id “itemTypes”跟踪项目类型名称 “brands”跟踪品牌名称。

如果我汇总订单 + 产品 + itemTypes 就可以了:

[
    $unwind: 
        path: '$orders'
    
, 
    $lookup: 
        from: 'products',
        localField: 'orders.productId',
        foreignField: 'productId',
        as: 'products'
    
, 
    $lookup: 
        from: 'itemTypes',
        localField: 'products.typeId',
        foreignField: 'typeId',
        as: 'itemTypes'
    
, 
    $set: 
        'orders.price': 
            $arrayElemAt: ['$products.price', 0]
        ,
        'orders.brandId': 
            $arrayElemAt: ['$products.brandId', 0]
        ,
        'orders.typeId': 
            $arrayElemAt: ['$products.typeId', 0]
        ,
        'orders.typeName': 
            $arrayElemAt: ['$itemTypes.name', 0]
        
    
, 
    $group: 
        _id: '$_id',
        createdAt: 
            $first: '$createdAt'
        ,
        status: 
            $first: '$status'
        ,
        retailerId: 
            $first: '$retailerId'
        ,
        retailerName: 
            $first: '$retailerName'
        ,
        orderId: 
            $first: '$orderId'
        ,
        orders: 
            $push: '$orders'
        
    
]

如果我聚合订单 + 产品 + itemTypes + 品牌,无论是 Mongo Compass 还是 Mongo Atlas 聚合构建器的 Web UI 都会出现操作超时错误。

[
    $unwind: 
        path: '$orders'
    
, 
    $lookup: 
        from: 'products',
        localField: 'orders.productId',
        foreignField: 'productId',
        as: 'products'
    
, 
    $lookup: 
        from: 'itemTypes',
        localField: 'products.typeId',
        foreignField: 'typeId',
        as: 'itemTypes'
    
, 
    $lookup: 
        from: 'brands',
        localField: 'products.brandId',
        foreignField: 'brandId',
        as: 'brands'
    
, 
    $set: 
        'orders.price': 
            $arrayElemAt: ['$products.price', 0]
        ,
        'orders.brandId': 
            $arrayElemAt: ['$products.brandId', 0]
        ,
        'orders.typeId': 
            $arrayElemAt: ['$products.typeId', 0]
        ,
        'orders.typeName': 
            $arrayElemAt: ['$itemTypes.name', 0]
        ,
        'orders.brandName': 
            $arrayElemAt: ['$brands.name', 0]
        
    
, 
    $group: 
        _id: '$_id',
        createdAt: 
            $first: '$createdAt'
        ,
        status: 
            $first: '$status'
        ,
        retailerId: 
            $first: '$retailerId'
        ,
        retailerName: 
            $first: '$retailerName'
        ,
        orderId: 
            $first: '$orderId'
        ,
        orders: 
            $push: '$orders'
        
    
]

这是一个超时聚合的演示:

https://mongoplayground.net/p/Jj6EhSl58MS

我们有大约 5 万个订单、1.4 万种产品、200 个品牌、89 种商品类型。

有没有办法优化这个聚合使其不会超时?

P/s:我的最终目标是使用 Mongodb Charts 功能中的漂亮图表可视化流行品牌和订购的商品类型。

【问题讨论】:

【参考方案1】:

你是否有关于你 $lookup 的集合的索引: 产品 (productId) + itemTypes (typeId) + 品牌 (brandId)。 否则,查找可能需要很长时间才能完成。

【讨论】:

有和没有品牌查询的执行时间是多少?你现在是从 MongoDB Charts 调用这个吗?也许你已经达到了 MongoDB Charts 的超时限制。 我可以看到有索引,但我担心如果我像上面那样查询所有 50k+ 个订单对象,它确实会触发 MongoDB 图表的超时限制。我测试只查询最近一年的订单,然后我将不再达到超时限制。这是非常不幸的,因为我确实希望客户能够在图表可视化中使用他们 10 年的订单,但看起来我不能,除非我弄清楚如何像我在这里问的那样加快聚合。 一种更激进的方法是对数据进行非规范化,以便您在订单中包含更多数据,这样您就不必进行如此多的查找。这是 MongoDB 中相当常见的方法。【参考方案2】:

如果您使用的是 Mongo Atlas,则可以使用触发器在后台运行聚合查询 - 无论是在数据库更新时还是作为计划触发器 (https://docs.mongodb.com/realm/triggers/)。

当触发器运行时,您可以使用“$merge”操作将聚合管道的结果保存在新集合中。

exports = function() 
    const mongodb = context.services.get(CLUSTER_NAME);
    const orders = mongodb.db(DATABASE_NAME).collection("orders");
    const ordersSummary = mongodb.db(DATABASE_NAME).collection("orders.summary");
    
    const pipeline = [
        
            YOUR_PIPELINE
        ,
         $merge:  into: "orders.summary", on: "_id", whenMatched: "replace", whenNotMatched: "insert"  
    ];

    orders.aggregate(pipeline);
;

这样,您的图表将非常快,因为它们只需从新集合中进行简单查询。

【讨论】:

以上是关于MongoDB 聚合管道的操作超时的主要内容,如果未能解决你的问题,请参考以下文章

翻译MongoDB指南/聚合——聚合管道

mongoDB查询进阶聚合管道 -- 阶段操作符

MongoDB的聚合操作

MongoDB——聚合管道之$limit&$skip&$sort操作

MongoDB的聚合操作

MongoDB基础教程系列--第七篇 MongoDB 聚合管道