为什么添加索引会降低性能?

时间:2018-09-09 11:12:58

标签: mongodb indexing nosql

我正在评估以下查询的性能。

db.products_old.find({ regularPrice: { $lte: 200 } })

该馆藏的文档超过一百万,总计约0.15GB。


没有索引

这是预期的。必须进行全列扫描

"executionTimeMillis" : 1019,

"winningPlan" : {
    "stage" : "COLLSCAN",
    "filter" : {
        "regularPrice" : {
            "$lte" : 200
        }
    },
    "direction" : "forward"
},

索引{RegularPrice:1}

"executionTimeMillis" : 2842,

"winningPlan" : {
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "regularPrice" : 1
        },
        "indexName" : "regularPrice_1",
        "isMultiKey" : false,
        "multiKeyPaths" : {
            "regularPrice" : [ ]
        },
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 2,
        "direction" : "forward",
        "indexBounds" : {
            "regularPrice" : [
                "[-inf.0, 200.0]"
            ]
        }
    }
},

现在它使用索引,但是执行时间明显更糟。 为什么?

如果性能更差,为什么Mongo不使用COLLSCAN代替使用索引,这会降低执行速度? rejectedPlans为空,这表明甚至没有考虑其他计划。为什么?


Here's的完整allPlansExecution输出。

1 个答案:

答案 0 :(得分:1)

在进行COLLSCAN时,MongoDB正在从存储驱动器读取并将匹配的文档存储在RAM中,以备后用。另一方面,IXSCAN读取存储索引数据的索引和指向它们在存储驱动器上位置的指针。 (Here's a nice visualisation从幻灯片6到幻灯片20左右)

您的集合中有很多文档,但是索引中也有很多匹配的文档。存储在存储驱动器上的数据并不是以最佳方式存储(从索引中读取),因此,IXSCAN返回指向您查询的220k +文档的指针时,{{1 }}需要以随机访问方式从存储驱动器读取220k +次。太慢了另一方面,我假设FETCH正在执行顺序读取,这可能是逐页进行的,并且比COLLSCAN读取要快得多。

所以总结一下:不是索引使您减速,而是FETCH阶段。如果您仍想使用此索引并具有更快的查询执行时间,请使用FETCH,它只会添加一个快速的.select('-_id regularPrice')阶段并从索引中读取所有必需的字段。或者,如果您需要PROJECTION,则添加一个索引_id

关于为什么这部分,Mongo是否使用索引,即使它可以知道集合扫描更快:好吧,我认为,如果看到索引,它将使用它。但是您可以通过将{regularPrice: 1, _id: 1}传递给hint来使用force it to use collection scan