为什么MongoDB中索引数组上的正则表达式前缀查询速度慢?

时间:2013-03-21 08:47:22

标签: mongodb

我正在尝试对MongoDB集合中的字符串数组执行正则表达式查询。我只能在docs

中找到此限制
  

$ regex只能在正则表达式时有效地使用索引   有一个锚点作为字符串的开头(即^),是一个   区分大小写的匹配

让我们做一个测试:

> for (var i=0; i<100000; i++) db.test.insert({f: ['a_0_'+i, 'a_1_2']})
> db.test.count()
100000
> db.test.ensureIndex({f: 1})
> db.test.find({f: /^a_(0)?_12$/ })
{ "_id" : ObjectId("514ac59886f004fe03ef2a96"), "f" : [ "a_0_12", "a_1_2" ] }
> db.test.find({f: /^a_(0)?_12$/ }).explain()
{
    "cursor" : "BtreeCursor f_1 multi",
    "isMultiKey" : true,
    "n" : 1,
    "nscannedObjects" : 200000,
    "nscanned" : 200000,
    "nscannedObjectsAllPlans" : 200000,
    "nscannedAllPlans" : 200000,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 482,
    "indexBounds" : {
        "f" : [
            [
                "a_",
                "a`"
            ],
            [
                /^a_(0)?_12$/,
                /^a_(0)?_12$/
            ]
        ]
    },
    "server" : "someserver:27017"
}

查询是sloooow。另一方面,此查询是最佳的:(但不适合我的用例)

> db.test.find({f: 'a_0_12' }).explain()
{
    "cursor" : "BtreeCursor f_1",
    "isMultiKey" : true,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 1,
    "nscannedAllPlans" : 1,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "f" : [
            [
                "a_0_12",
                "a_0_12"
            ]
        ]
    },
    "server" : "someserver:27017"
}

为什么正则表达式查询在有索引时扫描所有(子)记录?我错过了什么?

1 个答案:

答案 0 :(得分:1)

您的测试用例有几个对正则表达式和索引使用没有帮助的特性:

  • 每个文档包含两个值的数组,均以&#34; a _&#34;开头。您的正则表达式/^a_(0)?_12$/正在查找以a开头,后跟可选&#34; 0&#34;的字符串,因此会导致比较所有索引条目(200k值)。
  • 您的正则表达式还匹配每个文档具有的值(a_1_2),因此最终将匹配所有文档而不考虑索引

由于你有一个多键(数组索引),索引比较的数量实际上更糟而不仅仅是对100k文档进行全表扫描。您可以使用$natural提示进行测试,以查看:

db.test.find({f: /^a_(0|)12$/ }).hint({$natural:1}).explain()
{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 0,
    "nscannedObjects" : 100000,
    "nscanned" : 100000,
    "nscannedObjectsAllPlans" : 100000,
    "nscannedAllPlans" : 100000,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 192,
    "indexBounds" : {

    },
}

更多随机数据或更具选择性的正则表达式将导致更少的比较。