首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mongo不通过合并两个IXSCAN来优化$or查询

Mongo不通过合并两个IXSCAN来优化$or查询
EN

Stack Overflow用户
提问于 2017-08-18 18:52:41
回答 1查看 464关注 0票数 1

我有一个具有以下索引的orders集合,其中包括:

代码语言:javascript
复制
{location: 1, completedDate: 1, estimatedProductionDate: 1, estimatedCompletionDate: 1}

我正在执行以下查询:

代码语言:javascript
复制
db.orders.find({
  status: {$in: [1, 2, 3]},
  location: "PA",
  $or: [
    {completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}},
    {
      completedDate: null,
      estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
    }
  ]
}).explain()

我希望这将为$or的每个分支执行一个有效的$or,然后组合结果:

代码语言:javascript
复制
        {completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}}

        "indexBounds" : {
            "location" : [
                "[\"TX\", \"TX\"]"
            ],
            "completedDate" : [
                "[MinKey, ISODate("2017-08-22T04:59:59.999Z")]"
            ],
            "estimatedProductionDate" : [
                "[MinKey, MaxKey]"
            ],
            "estimatedCompletionDate" : [
                "[MinKey, MaxKey]"
            ]
        }

        {
            completedDate: null,
            estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
        }

        "indexBounds" : {
            "location" : [
                "[\"TX\", \"TX\"]"
            ],
            "completedDate" : [
                "[null, null]"
            ],
            "estimatedProductionDate" : [
                "[MinKey, ISODate("2017-08-22T04:59:59.999Z")]"
            ],
            "estimatedCompletionDate" : [
                "[MinKey, MaxKey]"
            ]
        }

相反,它只将location限制在IXSCAN中,并在FETCH期间执行其余的筛选。有没有办法优化这个查询而不将它分割成两个单独的查询?

代码语言:javascript
复制
"winningPlan" : {
    "stage" : "FETCH",
    "filter" : {
        "$and" : [
            {
                "$or" : [
                    {
                        "$and" : [
                            {
                                "completedDate" : {
                                    "$eq" : null
                                }
                            },
                            {
                                "estimatedProductionDate" : {
                                    "$lt" : "2017-08-22T04:59:59.999Z"
                                }
                            }
                        ]
                    },
                    {
                        "completedDate" : {
                            "$lt" : "2017-08-22T04:59:59.999Z"
                        }
                    }
                ]
            },
            {
                "status" : {
                    "$in" : [
                        1,
                        2,
                        3
                    ]
                }
            }
        ]
    },
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "location" : 1,
            "completedDate" : 1,
            "estimatedProductionDate" : 1,
            "estimatedCompletionDate" : 1
        },
        "indexName" : "location_1_completedDate_1_estimatedProductionDate_1_estimatedCompletionDate_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "location" : [
                "[\"TX\", \"TX\"]"
            ],
            "completedDate" : [
                "[MinKey, MaxKey]"
            ],
            "estimatedProductionDate" : [
                "[MinKey, MaxKey]"
            ],
            "estimatedCompletionDate" : [
                "[MinKey, MaxKey]"
            ]
        }
    }
},
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-08-21 01:55:46

有三个问题立即显现出来:

你的索引

我不确定您有哪些其他索引,但是您的查询是这样的:

代码语言:javascript
复制
{
  status:1,
  location:1,
  $or: [
    {completedDate:1},
    {completedDate:1, estimatedProductionDate:1}
  ]
}

但是,索引不包含术语status。您需要索引中的status字段来最大限度地使用索引。

您的$or查询

转述页面$or子句和索引

..。要使MongoDB使用索引计算$or表达式,索引必须支持$or表达式中的所有子句。否则,MongoDB将执行集合扫描。

简单地说,高效的$or查询在MongoDB中将要求$or术语成为顶级术语,该术语的每个部分都由一个索引支持。

例如,您可能会发现以下索引和查询的性能要好一些:

代码语言:javascript
复制
db.orders.createIndex({
  status:1,
  location:1,
  completedDate:1,
  estimatedProductionDate:1
})

db.orders.explain().find({
  $or: [
    {
      status: {$in: [1, 2, 3]},
      location: "PA",
      completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}},
    {
      status: {$in: [1, 2, 3]},
      location: "PA",
      completedDate: null,
      estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
    }
  ]
})

原因是因为MongoDB将$or查询中的每个术语视为单独的查询。因此,每个术语都可以使用自己的索引。

请注意,我前面建议的索引中字段的顺序遵循查询中字段的顺序。

但是,这仍然不是最优的,因为MongoDB必须在索引扫描completedDate: null查询之后使用filter: {completedDate: {$eq: null}}执行fetch操作。其原因是微妙的,也是解释得最好的这里

  1. 文档{}为键模式{"a.b":1}的索引生成索引键{"":null}。
  2. 文档{a:[]}还为键模式{"a.b":1}的索引生成索引键{"":null}。
  3. 文档{}匹配查询{"a.b":null}。
  4. 文档{a:[]}与查询{"a.b":null}不匹配。

因此,由键模式{"a.b":1}的索引回答的查询{"a.b":null}必须获取文档并重新检查谓词,以确保文档{}包含在结果集中,并且文档{a:[]}不包含在结果集中。

为了最大限度地利用索引,您最好只在completedDate字段中分配一些东西,而不是将其设置为null

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45763318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档