为什么 MongoDB 不使用索引交集?

Why MongoDB doesn't use Index Intersection?

我正在尝试重现索引交集指令的第一个示例 (http://docs.mongodb.org/manual/core/index-intersection/) 但面临一个问题:mongo 没有同时使用两个索引

我的步数:

  1. 下载mongo (3.0.3) 并安装
  2. 运行 mongod: mongod.exe --dbpath d:\data (文件夹为空)
  3. 运行 mongo: mongo.exe
  4. 添加索引:

    db.orders.ensureIndex({ qty: 1 })
    db.orders.ensureIndex({ item: 1 })
    db.orders.getIndexes()
    [{
            "v" : 1,
            "key" : {
                    "_id" : 1
            },
            "name" : "_id_",
            "ns" : "test.orders"
    },
    {
            "v" : 1,
            "key" : {
                    "qty" : 1
            },
            "name" : "qty_1",
            "ns" : "test.orders"
    },
    {
            "v" : 1,
            "key" : {
                    "item" : 1
            },
            "name" : "item_1",
            "ns" : "test.orders"
    }]
    
  5. 检查查询说明:

    db.orders.find( { item: "abc123", qty: { $gt: 15 } } ).explain()
    {
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.orders",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "item" : {
                                                "$eq" : "abc123"
                                        }
                                },
                                {
                                        "qty" : {
                                                "$gt" : 15
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "KEEP_MUTATIONS",
                        "inputStage" : {
                                "stage" : "FETCH",
                                "filter" : {
                                        "qty" : {
                                                "$gt" : 15
                                        }
                                },
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "keyPattern" : {
                                                "item" : 1
                                        },
                                        "indexName" : "item_1",
                                        "isMultiKey" : false,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "item" : [
                                                        "[\"abc123\", \"abc123\"]"
                                                ]
                                        }
                                }
                        }
                },
                "rejectedPlans" : [
                        {
                                "stage" : "KEEP_MUTATIONS",
                                "inputStage" : {
                                        "stage" : "FETCH",
                                        "filter" : {
                                                "item" : {
                                                        "$eq" : "abc123"
                                                }
                                        },
                                        "inputStage" : {
                                                "stage" : "IXSCAN",
                                                "keyPattern" : {
                                                        "qty" : 1
                                                },
                                                "indexName" : "qty_1",
                                                "isMultiKey" : false,
                                                "direction" : "forward",
                                                "indexBounds" : {
                                                        "qty" : [
                                                                "(15.0, 1.#INF]"
                                                        ]
                                                }
                                        }
                                }
                        }
                ]
        },
        "serverInfo" : {
                "host" : "localhost",
                "port" : 27017,
                "version" : "3.0.3",
                "gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105"
        },
        "ok" : 1
    }
    

如您所见,winningPlan 仅包含 item_1 索引。有包含 qty_1 索引的 rejectedPlans。但是没有包含索引交集的计划。 我知道 select 特定索引有很多条件。但就我而言 mongo 甚至没有计划!

有人能帮帮我吗?

SERVER-3071 JIRA issue 中有一些关于索引选择的详细信息,但我不能说是否所有内容都与 3.0 相关。不管怎样:

MongoDB 3.0.2 似乎 不考虑 range 查询的索引交互。但它将用于点间隔:

> db.orders.find( { item: {$eq : "abc123"}, qty: { $eq: 15 } } ).explain()
...

        {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "KEEP_MUTATIONS",
                "inputStage" : {
                    "stage" : "AND_SORTED",
                    "inputStages" : [
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "qty" : 1
                            },
                            "indexName" : "qty_1",
                            "isMultiKey" : false,
                            "direction" : "forward",
                            "indexBounds" : {
                                "qty" : [
                                    "[15.0, 15.0]"
                                ]
                            }
                        },
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "item" : 1
                            },
                            "indexName" : "item_1",
                            "isMultiKey" : false,
                            "direction" : "forward",
                            "indexBounds" : {
                                "item" : [
                                    "[\"abc123\", \"abc123\"]"
                                ]
                            }
                        }
                    ]
                }

我们正在托管一个多租户网站。所以有一个 mongo 集合包含所有客户的一些信息,例如

{customerId: 22, category: "category", region: "region", balance: 23434... }

在了解了Index Intersection之后,我们尝试创建多个单字段索引来支持不同查询条件的可变字段查询,比如

{ customerId: 22, category: "1" }
{ customerId: 22, region: "somewhere" }
{ customerId: 22, balance: {$gt:0} }
{ customerId: 22, region: {$in: ["R1", "R2"]},balance: {$gt:0} }
....

但从 "db.collection.explain" 中学习,结果并没有发生交集。 由于字段有限,我们最终找到了构建包含所有字段的复合索引的解决方案。

{customerId: 1, category: 1, region: 1, balance:1...}

然后发现下面所有查询都使用 "large" 复合索引,只要 "customerId" 在查询中。

{ customerId: 22, category: "1" }
{ customerId: 22, region: "somewhere" }
{ customerId: 22, balance: {$gt:0} }
{ customerId: 22, region: {$in: ["R1", "R2"]},balance: {$gt:0} }
....