Skip to content
On this page

聚合管道优化

1、$group 不使用 索引,想要使用 要借助 $match、$sort

2、$match 、 $sort 都可以 使用 索引,但是 key 要 小于等于 索引

3、$match 和 $sort 共存时,聚合优化器 会自动把 $match 提到 $sort 之前

4、可以使用 $match 强行 匹配索引,里面 有的 字段 不确定的 条件为 不等于 1 个 不太可能 中签的 值

实例

1、想实现 按 type 和 vertical 聚合操作,然后 统计 数量

按 type vertical 创建索引

db.getCollection('articleCollection').aggregate([
{
    $group: {
    _id: {
        type: "$type",
        vertical: "$vertical"
    },
    count: {$sum: 1}
    }
}])

数据库 里 有不到 5w 条 数据, 然后需要 6s!

2、通过 explain 分析,发现没有使用索引

db.getCollection('articleCollection').aggregate([
{
    $group: {
    _id: {
        type: "$type",
        vertical: "$vertical"
    },
    count: {$sum: 1}
    }
}], {explain: true})

通过 查看 winningPlan 里的 stage 为 COLLSCAN, 意思是 未使用 索引

{
    ...,
    "winningPlan" : {
        "stage" : "COLLSCAN",
        "direction" : "forward"
    }
    ...,
}

3、经过 查询文档,发现 $group 是不使用 索引,要想使用 得需要 使用 别的办法

这里用了 $sort

db.getCollection('articleCollection').aggregate([
    {
        $sort: {
            type : 1 ,
            vertical : 1
         }
     },
     {
         $group: {
            _id: {
               type: "$type",
               vertical: "$vertical"
            },
            count: {$sum: 1}
         }
     }
])

查询 时间为 0.187s! 基本 能 解决了 我的问题

4、但是 我 还有 其他的 参数,比如 有个 score、pushTime, 都需要 作为 $match 参数 搜索

当 $sort 和 $match 一起使用时

db.getCollection('articleCollection').aggregate([
    {
        $sort: {
            type : 1 ,
            vertical : 1
         }
     },
    {
        $match: {
            score : {
                $gte: 100
            },
            pushTime : {
                $gte: 100
            }
         }
     },
     {
         $group: {
            _id: {
               type: "$type",
               vertical: "$vertical"
            },
            count: {$sum: 1}
         }
     }
])

发现 搜索时间 又变成了 6s!

经过查询文档,发现 mongodb 的 聚合优化器 会把 $match 提到 $sort 之前,导致了 不能使用索引

5、多次试验后 使用的 方案:

创建了 type vertical score publishTime 作为 索引

db.getCollection('articleCollection').aggregate([
    {
        $match: {
            type: {$ne: "随便写的"},
            vertical: {$ne: "随便写的"},
            score: {$gte: 100},
            publishTime: {$gte: 0}
         }
     },
     {
         $group: {
            _id: {
               type: "$type",
               vertical: "$vertical"
            },
            count: {$sum: 1}
         }
     }
])

其中 type vertical 只要 不等于 给定的值 就可以

成功 实现了 0.2s 内返回数据!

6、最后的 方案

经过 数据对比,发现 少统计了一部分数据

原来的 有的 数据 是 没有 score、publishTime 字段的,group 的时候 会变成 null, 但是 我们限定了 >= 0 ,所以 不能被 搜索到

索性 都改成 不等于 1 个值

db.getCollection('articleCollection').aggregate([
    {
        $match: {
            type: {$ne: "随便写的"},
            vertical: {$ne: "随便写的"},
            score: {$ne: "随便写的"},
            publishTime: {$ne: "随便写的"},
         }
     },
     {
         $group: {
            _id: {
               type: "$type",
               vertical: "$vertical"
            },
            count: {$sum: 1}
         }
     }
])

完美的实现了 我们的 需求