首页 > 解决方案 > 如何加快具有许多布尔字段的 mongo db 查询?

问题描述

假设我的收藏user是这样的:

[
   {
      "name":"user1",
      "u_id":"1",
      "is_happy":true,
      "like_baseball":false,
      "is_strong":true,
   },
   {
      "name":"user2",
      "u_id":"2",
      "is_happy":false,
      "like_baseball":false,
      "is_strong":true,
   },
   {
      "name":"user3",
      "u_id":"3",
      "is_happy":true,
      "like_baseball":false,
      "is_strong":false,
   },
   ...
]

此集合中有 1m 文档。我创建了两个索引:

1.

{
   "is_happy": 1,
   "like_baseball": 1,
   "is_strong": 1,
}

2.

{
   "u_id": 1,
}

我们都知道第一个索引不能帮助加快下面的查询,因为它的选择性很差:

db.user.find({
   is_happy: true,
   like_baseball: true,
   is_strong: false,
})

MongoDB 文档提供了两种处理不良选择性的方法

  1. 将一个集合分成两个集合。(在我的例子中,将快乐和不快乐的人分成两个集合。)但是,我有三个布尔字段,这使得分离任务变得困难。

  2. 创建字段和其他具有大量价值的字段的复合索引。(在我的例子中,我可以创建一个由三个布尔字段和 . 组成的复合索引u_id。)但是,这意味着我必须u_id在所有查询中都包含 ,这是我无法保证的。

由于这两种方式都不适合我,我想知道是否有另一种方法可以加快查询速度。谢谢你们!:)

标签: databasemongodbindexing

解决方案


听起来这可能是属性模式的一个很好的用途。有关详细信息,请参阅https://www.mongodb.com/blog/post/building-with-patterns-the-attribute-pattern ...

顺便说一句,分成两个集合可能不会提供您寻求的性能改进。

如果您有以下索引:

{
   "is_happy": 1,
   "like_baseball": 1,
   "is_strong": 1,
}

并发出以下查询...

db.baseball.find({ is_happy: true, like_baseball: true, is_strong: false })

运行解释计划显示 Keys Examined 和 nReturned 之间的比率很好 (1:1)。

db.baseball.find({ is_happy: true, like_baseball: true, is_strong: false }).explain("allPlansExecution")

所有计划执行结果:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "barrystuff.baseball",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "is_happy" : {
                        "$eq" : true
                    }
                },
                {
                    "is_strong" : {
                        "$eq" : false
                    }
                },
                {
                    "like_baseball" : {
                        "$eq" : true
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "is_happy" : 1,
                    "like_baseball" : 1,
                    "is_strong" : 1
                },
                "indexName" : "is_happy_1_like_baseball_1_is_strong_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "is_happy" : [ ],
                    "like_baseball" : [ ],
                    "is_strong" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "is_happy" : [
                        "[true, true]"
                    ],
                    "like_baseball" : [
                        "[true, true]"
                    ],
                    "is_strong" : [
                        "[false, false]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 3,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 3,
        "totalDocsExamined" : 3,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 3,
            "executionTimeMillisEstimate" : 0,
            "works" : 4,
            "advanced" : 3,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 3,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 3,
                "executionTimeMillisEstimate" : 0,
                "works" : 4,
                "advanced" : 3,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "is_happy" : 1,
                    "like_baseball" : 1,
                    "is_strong" : 1
                },
                "indexName" : "is_happy_1_like_baseball_1_is_strong_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "is_happy" : [ ],
                    "like_baseball" : [ ],
                    "is_strong" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "is_happy" : [
                        "[true, true]"
                    ],
                    "like_baseball" : [
                        "[true, true]"
                    ],
                    "is_strong" : [
                        "[false, false]"
                    ]
                },
                "keysExamined" : 3,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        },
        "allPlansExecution" : [ ]
    },
    "serverInfo" : {
        "host" : "Barry-MacBook-Pro.local",
        "port" : 27017,
        "version" : "4.0.6",
        "gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
    },
    "ok" : 1
}

虽然选择性可能不好,但这是数据的性质,查询仍然需要结果。如果您需要此查询并且想要更好的性能,您可能需要首先考虑垂直缩放,然后如果仍然不能满足您的需求,请考虑水平缩放。

如果数据模型稳定并且使用的字段名称一致,您可能可以根据需要使用覆盖查询。我怀疑您的实际需求并不像提供的示例那么微不足道。


推荐阅读