【问题标题】:Why MongoDB doesn't use Index Intersection?为什么 MongoDB 不使用索引交集?
【发布时间】:2015-08-18 01:18:30
【问题描述】:

我正在尝试重现索引交集指令的第一个示例 (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 索引的拒绝计划。但是没有包含索引交集的计划。 我知道选择特定索引有很多条件。但就我而言,mongo 甚至没有计划!

有人可以帮我吗?

【问题讨论】:

  • FWIW,与 MongoDB 3.0.2 相同
  • 数据库中有多少文档? explain(true) 说什么?查询需要多长时间?数据字段的分布情况如何?

标签: mongodb indexing database-indexes mongodb-indexes


【解决方案1】:

我们正在托管一个多租户站点。所以有一个 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...}

然后事实证明,下面所有查询都使用“大”复合索引,只要“customerId”在查询中。

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

【讨论】:

  • 只是添加这么大的复合索引并没有多大帮助。在您提供的四个查询示例中,最后三个仅使用复合索引的customerId 前缀,而第一个使用复合索引的customerId 加上category 前缀。如果您尝试为这四个查询建立索引,那么仅针对前两个字段的复合索引会更好。只是说'所以没有人会想到一个庞大的复合索引会加速对其中包含的任何字段的任意查询过滤:)
【解决方案2】:

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\"]"
                                ]
                            }
                        }
                    ]
                }

【讨论】:

  • 所以基本上索引交集似乎在现实生活中的查询中从未发生过?根据我的测试,对于超过 1 个项目的 $in 查询也不起作用:(
  • 观察与 v3.4.10 相同的行为
  • 进一步观察:hint() 不适用于复合索引的前缀(仅限整个复合索引);索引交集似乎不适用于复合索引前缀(仅限单个索引)
猜你喜欢
  • 2021-09-21
  • 2011-05-30
  • 2014-08-21
  • 1970-01-01
  • 2014-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多