一起飞系列之:浅谈Mongodb的索引优化

难度水平:初中级
适用人群:对Mongodb数据查询已经一定经验的码农,了解explain()的用法。
阅读时间:10分钟

缘起

本来是打算回答一个网友的问题,不过回复框太小而且功能不全(差评^o^),于是乎就成就了本文的产生。在这里我主要就是想跟大家分享一下我对Mongodb索引的粗浅理解,抛砖引玉,希望能对一些人有所帮助。

对于数据库的索引相信大家都不陌生,主要功能就是在数据库内提高针对特定信息的访问速度。那么在Mongodb里面索引是怎样被引用跟查找的呢?下面我来通过几个例子给大家分享一下我的心得体会。

Mongodb基础数据查询

举个栗子,按照城市查询车牌号码系统。为了方便演示,我在所有查询后面都加入了explain()函数。要注意explain的返回结果集中的一下选项:

  • cursor - 索引项
  • n - 查询结果的条目总数
  • nscanned - 扫描并读取的索引条目数(index)
  • nscannedObjects - 扫描并读取的全文条目数(documents)
  • indexOnly - 是否使用了covered indexes功能,宝宝不哭,后面有详述。
  • millis - 查询用时,单位是毫秒。

栗子1: 在没有索引的情况下查询全部北京车牌号码

db.carLicence.find({city:'Beijing'}).explain()

//返回结果如下:
{
  "cursor" : "BasicCursor",
  "n" : 1563247,
  "nscannedObjects" : 96539732,
  "nscanned" : 96539732,
  "indexOnly" : false,
  "millis" : 156, // 哭!
}

BasicCursor表示全文检索。上面的例子说明,在没有索引的情况下,该查询语句查询了整个数据集carLicence。该数据集包含了96539732数据,其中1563247属于北京车牌。整个用时为156毫秒.

小结: 一定要避免没有索引的全文查询


栗子2: 在没有索引的情况下,使用limit(1000)来查询北京车牌号码

db.carLicence.find({city:'Beijing'}).limit(1000).explain()

//返回结果如下:
{
  "cursor" : "BasicCursor",
  "n" : 1000,
  "nscannedObjects" : 18476396,
  "nscanned" : 18476396,
  "indexOnly" : false,
  "millis" : 85, // 擦干眼泪!
}

这个例子说明,Mongodb在搜索满1000个条目之后就停止继续检索了。

小结: 尽量使用limit()来处理你的数据查询


栗子3: 添加索引

因为我们只是需要得到北京市的车牌号码,所以在我们的例子中涉及到了2个索引信息,一个是城市city,另外一个就是车牌号码licence。那么我们来给这2个信息添加索引,来查询全部北京车牌号码。

注意:给2个以上的字段建立索引叫做Compound Index复合索引[多字段索引),hashed字段不能创建索引。建立方法如下db.carLicence.ensureIndex( { "city": 1, "licence": 1 } )
db.carLicence.find({city:'Beijing'}).explain()

//返回结果如下:
{
  "cursor" : "BasicCursor city_1_licence_1",
  "n" : 1563247,
  "nscannedObjects" : 1563247,
  "nscanned" : 1563247,
  "indexOnly" : false,
  "millis" : 47, // 只剩 擦!
}

相信看到这里,大家已经对所有的功效有了一个初步的认识了。

小结 - 尽量给涉及到的条目建立索引

那么,indexOnly的covered indexes功能会有什么样的提高呢?好让我们来看最后这个例子。


栗子4: 使用覆盖索引 Covered Indexes

db.carLicence.find({city:'Beijing'}, {licence:1, _id:0}).explain()

//返回结果如下:
{
  "cursor" : "BasicCursor city_1_licence_1",
  "n" : 1563247,
  "nscannedObjects" : 0,
  "nscanned" : 1563247,
  "indexOnly" : true, // 变了 变了 !
  "millis" : 41, // 擦!擦!擦!
}

最后这个例子涉及到了covered indexes(覆盖索引),通常来说,覆盖索引只在查询文档的索引字段时候使用。但是有几个特性跟附加条件要遵循.

  • 查询字段必须是索引字段
  • 要去掉_id因为是返回的Object. 通过_id:0来实现。
  • 索引字段不能是. 也就是说在explain()里面,如果isMultiKey:true的话indexOnly一定是false.

另外一个就是在最后一个栗子里面nscannedObjects是零,这是因为我们只是提取了索引里面的目录条licence,并不需要每一条的全部数据(document),加上"indexOnly" : true,所以, Mongodb直接从索引里面提取了数据licence,不用再去查询物理数据集carLicence得到相关全文信息。所以nscannedObjects为零。

小结 - 在只提单字段数据的前提下,要给对应的但字段建立索引索引

结语

粗浅的分析一下我对索引的理解跟一些优化体验。有兴趣的朋友可以共通研究。最后说一句,善用explain()能对你的优化查询有很大的益处。加油!

你可能感兴趣的:(mongodb)