【问题标题】:Unable to get a covered query to use index only in mongodb无法获取覆盖查询以仅在 mongodb 中使用索引
【发布时间】:2013-01-07 05:54:06
【问题描述】:

我正在尝试使用覆盖索引在我的使用 mongodb 的应用上实现词干文本搜索。

我设置了以下索引:

ensureIndex({st: 1, n: 1, _id: 1});

但是当我对查询运行 explain() 时,无论我做什么,我都无法让 indexOnly 读取为真。

db.merchants.find({st: "Blue"}, {n:1,_id:1}).explain()
{
    "cursor" : "BtreeCursor st_1_n_1__id_1",
    "nscanned" : 8,
    "nscannedObjects" : 8,
    "n" : 8,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
        "st" : [
            [
                "Blue",
                "Blue"
            ]
        ],
        "n" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ],
        "_id" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ]
    }
}

我已经发现索引中键的顺序很重要。例如,如果我使用 {_id, n:1, st:1} 它根本没有使用这个索引来执行查询。我还在某处读到,太少的文档可能会使用 explain() 触发不可预测的行为,因为多种策略同样快。但是在这种情况下,我看到它使用了正确的索引,但它不仅仅使用了索引。这是怎么回事?

我正在使用 mongoid,我相信 mongo 2.0.8。

更新:

改用 Mongoid v3.1.4 和 mongod v2.2

这是 mongod 从 mongoid 看到的查询: Mon Jul 15 10:47:26 [conn14] runQuery called spl_development.merchants { $query: { st: { $regex: "cr", $options: "i " } }, $explain: true } Mon Jul 15 10:47:26 [conn14] query spl_development.merchants query: { $query: { st: { $regex: "cr", $options: "i" } }, $explain: true } ntoreturn:0 keyUpdates:0 locks(micros) r:212 nreturned:1 reslen:393 0ms

所以投影不会被发送到 mongod 层,而只是在应用层处理它。不理想!

这已被认为是 mongoid 中的一个错误,可以在此处进行跟踪: https://github.com/mongoid/mongoid/issues/3142

【问题讨论】:

    标签: mongodb indexing mongoid


    【解决方案1】:

    我希望您的查询不能使用覆盖索引,因为您有一个包含在索引中的数组的字段。在解释中建议使用"isMultiKey" : true

    如文档中所述 (Create Indexes that Support Covered Queries):

    如果集合中任何文档中的任何索引字段包含数组,则 MongoDB 无法使用覆盖查询。如果索引字段是数组,则索引变为多键索引,不支持覆盖查询。

    【讨论】:

    • 感谢 Stennie,完全正确,'st' 是一个数组。我想为了实现这一点并确保它是一个涵盖速度的查询,我必须在文本字段上使用正则表达式,而我之前的策略是将文本拆分为数组中的单词。希望结果是从内存索引返回的事实来补偿正则表达式的缓慢性。
    • 您可以使用正则表达式来使其成为一个覆盖查询,但是为了有效地使用索引,正则表达式应该是左根的(例如/^Blue/)并且区分大小写。添加正则表达式时检查nscanned 值,以查看所需的索引比较次数与n(返回的结果数)。您还可以通过将关键字数组分成具有单个索引字段的多个文档来使其成为覆盖查询。我将对不同的方法进行基准测试,看看哪种方法最适合您的用例;多键(但未覆盖的索引)实际上可能很好:)
    • 另请注意,正在为 MongoDB 2.4 改进文本搜索功能(词干、评分、短语匹配、停用词等)(请参阅 SERVER-380)。您应该能够在即将发布的 2.3.2 开发版本中测试这一点(或者如果您不耐烦/好奇,则可以在夜间构建)。有人尝试过的博客文章:MongoDB Text Search Explained.
    【解决方案2】:

    我无法在 2.2.2 中重现该问题,但将 .sort({n: 1, _id: 1}) 添加到链中。因为您没有进行排序,所以您正在以任何find mongo 希望使用的顺序请求文档,如果这与索引中的顺序不匹配(例如,$natural 顺序)它仍然必须阅读文档。

    db.merchants.find({st: "Blue"}, {n:1,_id:1}).sort({n: 1, _id: 1}).explain()
    

    【讨论】:

    • 索引中的顺序与查询是否被覆盖无关。根据定义,覆盖查询是只能使用索引字段返回结果的查询。
    • 我知道你在说什么,但是当我看到这个时,我发现如果我将 .sort($natural: 1}) 添加到链中,它会导致解释返回 indexOnly: false否则为真。无论如何,我相信你的回答是真正的原因,所以 +1 给你!
    • 如果你只添加一个 $natural 排序,查询优化器可能会选择一个BasicCursor(表扫描)给定少量文档进行比较。您可以使用hint() 来确认在使用正确索引的情况下仍将覆盖排序查询:db.merchants.find({st: "Blue"}, {n:1,_id:1}).hint('st_1_n_1__id_1').sort({$natural:1}).explain()
    猜你喜欢
    • 1970-01-01
    • 2014-09-17
    • 2017-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多