【问题标题】:MongoDB Index Coverage with complicated query具有复杂查询的 MongoDB 索引覆盖率
【发布时间】:2020-10-09 18:17:32
【问题描述】:

Mongo DB 版本 3.4.6

我有一个文档结构类似于以下内容的集合:

{
  organization: "ABC123",
  tags: ["MARTHA WASHINGTON", "+15552082000"],
  updatedAt : ISODate("2020-10-09T17:19:44.861Z"),
  createdAt : ISODate("2020-01-14T19:46:15.957Z"),
}

我需要能够按组织和标签数组上的正则表达式“开头”进行查询,并且可以选择按updatedAt 或createdAt 排序。为此,我创建了以下索引:

{
    "organization" : 1,
    "tags" : 1,
    "createdAt" : -1
}

这是一个多键复合索引,根据我对 Mongo 的理解,它应该允许我涵盖所有情况下的查询。如果我执行如下查询:

db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/})

查询被索引覆盖 - 我看到一个 FETCH/IXSCAN 阶段。

同样,如果我删除正则表达式查询并添加排序 - 查询将被完美覆盖。

db.getCollection('data').find({"organization": "ABC123", "search": "MARTHA WASHINGTON"}).sort({"createdAt":-1})

但是,如果我结合正则表达式和排序选项,我会突然在查询中看到一个额外的 SORT 阶段。示例查询:

db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/}).sort({"createdAt":-1})

这是解释中的获胜计划输出:

"winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "createdAt" : -1.0
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "organization" : 1,
                            "tags" : 1,
                            "createdAt" : -1
                        },
                        "indexName" : "tag matches by organization",
                        "isMultiKey" : true,
                        "multiKeyPaths" : {
                            "organization" : [],
                            "search" : [ 
                                "search"
                            ],
                            "createdAt" : []
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "organization" : [ 
                                "[\"ABC123\", \"ABC123\"]"
                            ],
                            "tags" : [ 
                                "[\"MARTHA WASHINGTON\", \"MARTHA WASHINGTOO\")", 
                                "[/^MARTHA WASHINGTON/, /^MARTHA WASHINGTON/]"
                            ],
                            "createdAt" : [ 
                                "[MaxKey, MinKey]"
                            ]
                        }
                    }
                }
            }
        },

我很困惑为什么这个查询组合没有被索引覆盖。我的理解是,一开始的额外排序阶段会导致大型集合的性能下降。任何人都可以提供一些指导吗?我错过了一些限制吗?

更新:删除正则表达式查询时的获胜计划

   "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "organization" : 1,
                    "search" : 1,
                    "createdAt" : -1
                },
                "indexName" : "tag matches by organization",
                "isMultiKey" : true,
                "multiKeyPaths" : {
                    "organization" : [],
                    "search" : [ 
                        "search"
                    ],
                    "createdAt" : []
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "organization" : [ 
                        "[\"ABC123\", \"ABC123\"]"
                    ],
                    "tags" : [ 
                        "[\"MARTHA WASHINGTON\", \"MARTHA WASHINGTON\"]"
                    ],
                    "createdAt" : [ 
                        "[MaxKey, MinKey]"
                    ]
                }
            }
        },

【问题讨论】:

  • 覆盖查询的查询计划是什么?
  • @D.SM - 我已根据您的要求添加了获胜计划
  • 刚看到版本号 - 请在 4.4.1 上测试。
  • 我认为在这种情况下版本号无关紧要。

标签: mongodb mongodb-query


【解决方案1】:

另一个答案不太准确。来自docs

对于区分大小写的正则表达式查询,如果字段存在索引,则 MongoDB 将正则表达式与索引中的值进行匹配,这可能比集合扫描更快。

Mongo 能够使用带有正则表达式的索引,显然,如果您的正则表达式是后缀正则表达式,那么集合扫描实际上可能会更快,因为 Mongo 必须读取整个索引树才能满足要求。

那么您的查询中发生了什么?为什么中奖计划是sort?好吧,虽然它可能实际上是获取结果的最佳方式,但也有可能 Mongo 只是选择了错误的计划。

首先让我们了解 Mongo 如何选择获胜计划,计划评估基于比较给定查询的候选计划,以查看哪个返回第一批结果(默认为 101 个文档)且总体“工作量”最少.作品分数代表查询阶段(索引键比较、获取文档等)中涉及的不同工作量。如果多个计划在评估期间执行相同的工作,则有一些小的平局奖金可以帮助选择要缓存的计划。基本上,Mongo 会进行一场小型“比赛”,然后等待谁获胜。

因此,在您的情况下,由于具有索引的正则表达式性质,sort 阶段获胜,如果您完全运行计划而不是小样本,则可能会选择不同的计划。

我建议您使用hint 进行自己的测试,这会强制 Mongo 使用某个索引,这意味着您可以强制 Mongo 为您的查询制定获胜计划。我个人认为(显然取决于特定的正则表达式)您可以通过这样做来提高性能,因为首先排序几乎不是每个“最佳”计划。

【讨论】:

    【解决方案2】:

    假设您在集合中有两个字段:姓名和年龄,并且您按姓名过滤并按年龄排序。

    假设您有以下文件:

    JON 30
    JON 45
    JONATHAN 40
    

    假设您在 (name, age) 上创建一个索引。该索引对上面列出的文档进行排序。

    如果查询name = JON并按年龄排序,所有条件都与索引完全匹配,(JON, 30), (JON, 45)的输出只能通过索引遍历获得。

    如果您查询name =~ ^JON 并按年龄排序,您期望的输出现在是(JON, 30), (JONATHAN, 40), (JON, 45)。由于名称匹配现在不精确,因此索引中不存在此排序,因此服务器必须对结果集进行排序以提供它。

    【讨论】:

    • 谢谢,我认为这是有道理的。随着集合规模的扩大,在这种情况下添加排序有多“糟糕”?既然它发生在索引上,那可以吗,因为它在内存中?
    • 服务器只会对符合条件的文档进行排序。如果数量很少,则问题不大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 2019-04-15
    • 2013-12-18
    • 1970-01-01
    • 2017-01-12
    • 2022-01-13
    • 2014-11-13
    相关资源
    最近更新 更多