【问题标题】:MongoDB - Index Intersection with two multikey indexesMongoDB - 具有两个多键索引的索引交集
【发布时间】:2014-04-18 04:57:48
【问题描述】:

我的集合中有两个数组(一个是嵌入式文档,另一个只是简单的字符串集合)。以文档为例:

{
    "_id" : ObjectId("534fb7b4f9591329d5ea3d0c"),
    "_class" : "discussion",
    "title" : "A",
    "owner" : "1",
    "tags" : ["tag-1", "tag-2", "tag-3"],
    "creation_time" : ISODate("2014-04-17T11:14:59.777Z"),
    "modification_time" : ISODate("2014-04-17T11:14:59.777Z"),
    "policies" : [
        {
            "participant_id" : "2",
            "action" : "CREATE"
        }, {
            "participant_id" : "1",
            "action" : "READ"
        }
    ]
}

由于某些查询将仅包含策略,而某些查询将包含标签和参与者数组,并且考虑到我无法使用两个数组创建多键索引这一事实,我认为这将是一个经典场景使用Index Intersection

我正在执行查询,但我看不到交叉点开始。

这里是索引:

db.discussion.getIndexes()

   {
           "v" : 1,
           "key" : {
                   "_id" : 1
           },
           "name" : "_id_",
           "ns" : "test-fw.discussion"
   },
   {
           "v" : 1,
           "key" : {
                   "tags" : 1,
                   "creation_time" : 1
           },
           "name" : "tags",
           "ns" : "test-fw.discussion",
           "dropDups" : false,
           "background" : false
   },
   {
           "v" : 1,
           "key" : {
                   "policies.participant_id" : 1,
                   "policies.action" : 1
           },
           "name" : "policies",
           "ns" : "test-fw.discussion"
   }

这里是查询:

db.discussion.find({ 
    "$and" : [ 
        { "tags" : { "$in" : [ "tag-1" , "tag-2" , "tag-3"] }},
        { "policies" : { "$elemMatch" : { 
            "$and" : [ 
                { "participant_id" : { "$in" : [ 
                    "participant-1",
                    "participant-2", 
                    "participant-3"
                ]}}, 
                { "action" : "READ"}
            ]
        }}}
    ]
})
.limit(20000).sort({ "creation_time" : 1 }).explain();

这里是解释的结果:

      "clauses" : [
          {
                  "cursor" : "BtreeCursor tags",
                  "isMultiKey" : true,
                  "n" : 10000,
                  "nscannedObjects" : 10000,
                  "nscanned" : 10000,
                  "scanAndOrder" : false,
                  "indexOnly" : false,
                  "nChunkSkips" : 0,
                  "indexBounds" : {
                          "tags" : [
                                  [
                                          "tag-1",
                                          "tag-1"
                                  ]
                          ],
                          "creation_time" : [
                                  [
                                          {
                                                  "$minElement" : 1
                                          },
                                          {
                                                  "$maxElement" : 1
                                          }
                                  ]
                          ]
                  }
          },
          {
                  "cursor" : "BtreeCursor tags",
                  "isMultiKey" : true,
                  "n" : 10000,
                  "nscannedObjects" : 10000,
                  "nscanned" : 10000,
                  "scanAndOrder" : false,
                  "indexOnly" : false,
                  "nChunkSkips" : 0,
                  "indexBounds" : {
                          "tags" : [
                                  [
                                          "tag-2",
                                          "tag-2"
                                  ]
                          ],
                          "creation_time" : [
                                  [
                                          {
                                                  "$minElement" : 1
                                          },
                                          {
                                                  "$maxElement" : 1
                                          }
                                  ]
                          ]
                  }
          },
          {
                  "cursor" : "BtreeCursor tags",
                  "isMultiKey" : true,
                  "n" : 10000,
                  "nscannedObjects" : 10000,
                  "nscanned" : 10000,
                  "scanAndOrder" : false,
                  "indexOnly" : false,
                  "nChunkSkips" : 0,
                  "indexBounds" : {
                          "tags" : [
                                  [
                                          "tag-3",
                                          "tag-3"
                                  ]
                          ],
                          "creation_time" : [
                                  [
                                          {
                                                  "$minElement" : 1
                                          },
                                          {
                                                  "$maxElement" : 1
                                          }
                                  ]
                          ]
                  }
          }
  ],
  "cursor" : "QueryOptimizerCursor",
  "n" : 20000,
  "nscannedObjects" : 30000,
  "nscanned" : 30000,
  "nscannedObjectsAllPlans" : 30203,
  "nscannedAllPlans" : 30409,
  "scanAndOrder" : false,
  "nYields" : 471,
  "nChunkSkips" : 0,
  "millis" : 165,
  "server" : "User-PC:27017",
  "filterSet" : false

查询中的每个标签(tag1、tag-2 和 tag-3)都有 10K 个文档。 每个策略({participant-1,READ},{participant-2,READ},{participant-3,READ})都有 10K 个文档。

AND 运算符的结果是 20K 文档。

正如我之前所说,我不明白为什么两个索引(我的意思是策略和标签索引)的交集没有起作用。

有人可以为我遗漏的东西遮遮掩掩吗?

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    有两件事对您理解这一点实际上很重要。

    • 第一点是查询优化器在解析查询计划时只能使用一个索引,不能同时使用你指定的两个索引。因此,它会根据自己的决定选择最“适合”的那个,除非您使用hint 明确指定它。交叉点有点适合,但现在是下一点:

    • 第二点记录在复合索引的limitations 中。这实际上指出,即使您要“尝试”创建一个包含您想要的两个数组字段的复合索引,那么您也做不到。这里的问题是,作为一个数组,这为边界键引入了太多的可能性,而多键索引在与标准字段复合使用时已经引入了相当程度的复杂性。

    组合两个多键索引的限制是这里的主要问题,就像在创建时一样,“组合”两者的复杂性产生了两个多排列,使其成为一个可行的选择。

    policies 索引实际上可能更适合用于此类搜索,您可能可以通过首先在查询中指定该字段来修改这一点:

    db.discussion.find({
        { 
            "policies" : { "$elemMatch" : { 
                "participant_id" : { "$in" : [ 
                    "participant-1", 
                    "participant-2",
                    "participant-3"
                ]},
                "action" : "READ"
            }},
            "tags" : { "$in" : [ "tag-1" , "tag-2" , "tag-3"] }
        }
    )
    

    如果这将选择较小范围的数据,它可能会这样做。否则使用前面提到的hint 修饰符。

    如果这实际上并不能直接帮助结果,则可能值得重新考虑架构,使其不涉及将这些值包含在数组字段或其他类型的“元”字段中,这些字段可以很容易地用索引。

    还要注意,在编辑后的表单中,所有包装 $and 语句都不应该是必需的,因为“and”在 MongoDB 查询中是隐含的。作为修饰符,仅当您希望同一字段有两个 不同条件时才需要它。

    【讨论】:

    • 感谢您的详细解答。为了清楚起见,我说的是索引交集功能(2.6 中的新功能)。复合指数的局限性非常明显。我没有看到 Index Intersection 文档有任何限制(直接链接在最初的问题上)。我仍然找不到复合索引限制和我的用例之间的关系。你能和我分享一个可以解释它的资源吗?
    • @MordechaiTamam 明白了,但处理多键索引时的基本原理是相同的。我不认为当前的交集方法完全可以处理各种多键索引。这是从关系世界借来的概念,其中不存在多键之类的概念。因此,为什么我将其与复合指数进行比较,因为一般原则是相同的。
    • @MordechaiTamam 一直在做其他事情,所以延迟了。但是为了更清楚一点(遗憾的是,这并没有硬性参考,新事物通常就是这种情况),但考虑一下文本或地理空间索引之类的东西。您将如何建议这些索引将相交,例如使用其中的两个或多个(如果甚至可以发出该查询)?我们实际上在谈论相同的基本事物,因为这些复杂类型不适合索引交集的原则。这会很好,但我很高兴我没有为它编写代码。
    • 谢谢,我希望我们能够从一位 MongoDB 工程师那里得到具体的答案(无意冒犯 :))
    【解决方案2】:

    经过一点测试,我相信Mongo实际上可以在一个交集中使用两个多键索引。我创建了一个具有以下结构的集合:

    {
        "_id" : ObjectId("54e129c90ab3dc0006000001"),
        "bar" : [
            "hgcmdflitt",
            ...
            "nuzjqxmzot"
        ],
        "foo" : [
            "bxzvqzlpwy",
            ...
            "xcwrwluxbd"
        ]
    }
    

    我在 foo 和 bar 上创建了索引,然后运行了以下查询。注意传入的“true”进行解释。这将启用详细模式。

    db.col.find({"bar":"hgcmdflitt", "foo":"bxzvqzlpwy"}).explain(true)
    

    在详细结果中,您可以找到响应的“allPlans”部分,该部分将显示 mongo 考虑的所有查询计划。

    "allPlans" : [
        {
            "cursor" : "BtreeCursor bar_1",
            ...
        },
        {
            "cursor" : "BtreeCursor foo_1",
            ...
        },
        {
            "cursor" : "Complex Plan"
            ...
        }
    ]
    

    如果您看到带有“光标”的计划:“复杂计划”表示 mongo considered using an index intersection。要查找 mongo 可能未决定实际使用该查询计划的原因,请参阅此答案:Why doesn't MongoDB use index intersection?

    【讨论】:

    猜你喜欢
    • 2014-08-21
    • 1970-01-01
    • 2013-12-15
    • 2019-12-21
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    相关资源
    最近更新 更多