美文网首页
Mongo索引-$or

Mongo索引-$or

作者: Robin92 | 来源:发表于2019-11-20 20:32 被阅读0次

MongoDB 中索引因为 $or 引发过多次问题,最近又有新发现,所以决定趁此机会,将发现总结在这里。

综述

规则一:正如 文档 表述,$or 数组中的每一个对象都应当能完整地命中一个索引。
规则二:用于 $or 内的字段同时出现在与 $or 并列的位置,且字段是索引人关键字段时,会影响索引的命中情况。


详细解释

MongoDB 版本: 3.2.x

规则一:正如 文档 表述,$or 数组中的每一个对象都应当能完整地命中一个索引。

比如,当你有索引:

db.getCollection('membershipDiscount').createIndex({"accountId": 1, "member.phone": 1})
db.getCollection('membershipDiscount').createIndex({"accountId": 1, "member.name": 1})

此时,你需要用以下这种方式:

db.getCollection('membershipDiscount').find({
    "$or": [
        {
            "accountId": ObjectId("58d2112d829fa700204bbcc4"),
            "member.phone": "SearchKey"
        },
        {
            "accountId": ObjectId("58d2112d829fa700204bbcc4"),
            "member.name": "SearchKey"
        }
    ],
}).explain('executionStats')

它执行结果的 winningPlan 是这样子的:

"winningPlan" : {
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "OR",
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "member.phone" : 1
                    },
                    "indexName" : "accountId_1_member.phone_1",
                    "indexBounds" : {
                        "accountId" : [
                            "[ObjectId('58d2112d829fa700204bbcc4'), ObjectId('58d2112d829fa700204bbcc4')]"
                        ],
                        "member.phone" : [
                            "[\"SearchKey\", \"SearchKey\"]"
                        ]
                    }
                    // ...
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "member.name" : 1
                    },
                    "indexName" : "accountId_1_member.name_1",
                    "indexBounds" : {
                        "accountId" : [
                            "[ObjectId('58d2112d829fa700204bbcc4'), ObjectId('58d2112d829fa700204bbcc4')]"
                        ],
                        "member.name" : [
                            "[\"SearchKey\", \"SearchKey\"]"
                        ]
                    }
                    // ...
                }
            ]
        }
    }
},

但在使用时,在此基础上又发现了一个规则,即我们用如下这种方式时:

db.getCollection('membershipDiscount').find({
    "$or": [
        {
            "accountId": ObjectId("58d2112d829fa700204bbcc4"),
            "member.phone": "SearchKey"
        },
        {
            "accountId": ObjectId("58d2112d829fa700204bbcc4"),
            "member.name": "SearchKey"
        }
    ],
    "accountId": ObjectId("58d2112d829fa700204bbcc4"),
}).explain('executionStats')

它命中的索引只有一个 accountId, member.name,其 winningPlan 是这样子的:

"winningPlan" : {
    "stage" : "FETCH",
    "filter" : {
        // ...
    },
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "accountId" : 1,
            "member.name" : 1
        },
        "indexName" : "accountId_1_member.name_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "accountId" : [
                "[ObjectId('58d2112d829fa700204bbcc4'), ObjectId('58d2112d829fa700204bbcc4')]"
            ],
            "member.name" : [
                "[MinKey, MaxKey]"
            ]
        }
    }
},

而后,再将索引搜索的结果再逐个检查(keysExamined)每个 document 是否命中 member.phone 这个条件。

也就是说, 规则二:用于 $or 内的字段同时出现在与 $or 并列的位置,且字段是索引人关键字段时,会影响索引的命中情况。

那导致这个索引只命中一个的原因,会不会是因为命中了 $or 并列字段的索引,而不是 $or 的子句的索引,它命中索引会不会首选是外层条件能命中的索引。我又做了个实验,用一个表创建了三个索引:

db.getCollection('couponCode').createIndex({accountId: 1, status: 1})
db.getCollection('couponCode').createIndex({code: 1})
db.getCollection('couponCode').createIndex({lowerCode: 1})

然后用查询:

db.getCollection('couponCode').find({
    accountId: ObjectId('5c552c919cd0b306b62fbc82'),
    $or: [
        {code: "2t9s6df3b9jf4fdfsjqxwkso"},
        {lowerCode: "2t9s6df3b9jf4fdfsjqxwkso"}
    ]
}).explain('executionStats')

发现其 winningPlan 为:

"winningPlan" : {
    "stage" : "FETCH",
    "filter" : {
        "accountId" : {
            "$eq" : ObjectId("5c552c919cd0b306b62fbc82")
        }
    },
    "inputStage" : {
        "stage" : "OR",
        "inputStages" : [ 
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "code" : 1.0
                },
                "indexName" : "code_1",
                // ...
                "indexBounds" : {
                    "code" : [ 
                        "[\"2t9s6df3b9jf4fdfsjqxwkso\", \"2t9s6df3b9jf4fdfsjqxwkso\"]"
                    ]
                }
            }, 
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "lowerCode" : 1.0
                },
                "indexName" : "lowerCode_1",
                // ...
                "indexBounds" : {
                    "lowerCode" : [ 
                        "[\"2t9s6df3b9jf4fdfsjqxwkso\", \"2t9s6df3b9jf4fdfsjqxwkso\"]"
                    ]
                }
            }
        ]
    }
},

即在规则二中,与 $or 并列有匹配条件不是直接原因,我们在项目中也常常用这种条件去查询,仅 $or 内的子匹配字段与 $or 外的重复且就索引的关键字段时才会有这种情况。


在此,顺便补充一下 $regex 模糊查询对索引的利用效率:

  • i option(case insensitive) 的是无法利用索引的。(解决方法是存储时冗余一个存小写值的字段,查询时转换为小写查)
  • 性能从高到低: /^a//^a.*//^a.*$/a/
  • 文档 中有明确解释,说:“虽然 /^a//^a.*//^a.*$ 等价于相同的字符串匹配,但它们的性能是不一样的”。(注:. 用于匹配除换行符 \n 外的任何单字符)

相关文章

  • mongo回顾(四)

    上回聊到mongo索引采用了B树,而且采用的原因。今天具体聊聊mongo中的索引。MongoDB defines ...

  • mongo索引

       不使用索引的查询称为全表扫描。通常来说,应该尽量避免全表扫描,全表扫描的效率非常低。   创建索引: db....

  • Mongo索引-$or

    MongoDB 中索引因为 $or 引发过多次问题,最近又有新发现,所以决定趁此机会,将发现总结在这里。 综述 规...

  • mongodb索引

    mongodb的速度很大程度上取决于mongo的hash设计,而与此关系最密切的就是mongo的索引。 查看索引 ...

  • 优化网站加载速度-mongo篇

    笔者项目用到技术点 php+mongo+sql+redis mongo的索引 首先说明一下,使用mongo不加搜索...

  • mongo回顾(三:索引B+树)

    上回提到mongo怎么进行模式设计。今天就来聊聊mongo的索引B树。 https://docs.mongodb....

  • mongo复合索引

    1、复合索引创建语法 db.collection.createIndex( { : , : , ... } ) 同...

  • mongo的索引

    MongoDB 索引 createIndex() 方法 MongoDB使用 createIndex() 方法来创建...

  • mongo索引问题

    现象描述:系统重新部署后无法提供服务。问题分析:经过排查后,发现系统重启后多次调用创建mongo索引的API,导致...

  • Mongodb学习笔记 (五) 之 索引

    索引 索引提高查询速度,降低写入速度。权衡常用的查询字段,不必在太多列上建索引 在mongo中,索引可以按字段升序...

网友评论

      本文标题:Mongo索引-$or

      本文链接:https://www.haomeiwen.com/subject/fclaictx.html