mongo查询时候如何选择索引

yang9527
  • 138

问题描述

mongo中有以下两个索引,第一个索引是:{_id}字段的单字段索引;
第二个索引是{chat_id, _id}组合成的复合索引。当我使用查询
db.collection.find({"chat_id" : ObjectId("*********"), "the_time":{"$lt" : ISODate("2010-01-01T00:00:00Z")}}).sort({"the_time":-1}).explain("executionStats"),为什么会得到下面这种结果分析

......
"winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "the_time" : -1
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "filter" : {
                        "the_time" : {
                            "$lt" : ISODate("2010-01-01T00:00:00Z")
                        }
                    },
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "chat_id" : 1,
                            "_id" : 1
                        },
                        "indexName" : "chat_id_1__id_1"
......

为什么会使用到{chat_id, _id}组合成的复合索引?

我所了解到的复合索引是这样的

索引前缀指的是复合索引的子集
假如存在如下索引

{ "item": 1, "location": 1, "stock": 1 }

那存在下列索引前缀
{ item: 1 }
{ item: 1, location: 1 }

在MongoDB中,下列查询过滤条件情形中,索引将会被使用到
        item字段
        item字段 + location字段
        item字段 + location字段 + stock字段
        item字段 + location字段(尽管索引被使用,但不高效)

以下过滤条件查询情形,索引将不会被使用到
    location字段
    stock字段
    location + stock字段

问题一: 难道是,复合索引中{chat_id, _id},即使使用的查询是{chat_id, the_time}中the_time字段不在复合索引中,但是chat_id在复合索引中,所以就会走这个复合索引么?

问题二: 另外,我额外加了一个索引{chat_id,third_field},依然查询{chat_id,the_time},最后得到的分析结果走的是{chat_id,third_field}这个索引,却没有走{chat_id, _id}此索引。mongo在索引选择上,是什么策略?

回复
阅读 2.6k
1 个回答

问题一

  1. 如果查询可以命中索引,它就可以直接给出满足条件的所有文档的地址(IXSCAN),由于得到的是地址,不是文档本身,所以还需要一个额外的步骤从地址找出实际的文档(FETCH);
  2. 如果查询没有索引的支持,就只能把有可能满足条件的数据全部加载到内存,然后逐一比较是否满足条件,最终得出结果集(COLLSCAN);

如果走了第二条路,显然是相当耗费资源和时间的,因此我们所有的查询都要尽可能命中索引,或者部分命中索引。你的疑问可能是没有理解部分命中索引是怎么回事。假设你的集合有100w条记录,查询条件{"chat_id" : ObjectId("*********"), "the_time":{"$lt" : ISODate("2010-01-01T00:00:00Z")}}下:

  • 最坏的情况无非是没有任何索引支持,那么你需要遍历一遍100w条记录,看谁满足这两个条件得到结果集;
  • 但是现在有{chat_id: 1, _id: 1}存在,它虽然不能完全满足你的查询,但是第一个条件是满足的。假设这个条件帮你过滤了90w条记录(只剩10w条),那么其余条件就只需要在这10w条中遍历就好了,是不是比遍历100w条要更优呢?

如果选择了{_id: 1},对查询没有任何帮助,跟第一种情况是一样的;如果选择{chat_id: 1, _id: 1},至少可以有一定帮助,所以为什么不选择后者?

问题二

首先要理解一个问题,同一个条件就算使用相同的索引运行2次,执行时间也不一定就相同,因为服务器的压力情况不一定相同。那么你提到的两个索引{chat_id,third_field}{chat_id,the_time}它们对你的执行条件的作用几乎是没有什么差异的,先客观上两者取最高效者,总会选出一个更快者,一旦选出更快的一个,执行计划缓存会确保重启前一直使用它,而不是每次都来评估(浪费资源)。所以这里比较它们两个其实没有什么意义。

其他

db.collection.find({"chat_id" : ObjectId("*********"), "the_time":{"$lt" : ISODate("2010-01-01T00:00:00Z")}}).sort({"the_time":-1}).explain("executionStats")

满足这个查询的最佳索引应该是:{chat_id: 1, the_time: -1}

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏