转载请出自出处:http://eksliang.iteye.com/blog/2178555
一、概述
上一篇文档中也说明了,MongoDB的索引几乎与关系型数据库的索引一模一样,优化关系型数据库的技巧通用适合MongoDB,所有这里只讲MongoDB需要注意的地方
二、索引内嵌文档
可以在嵌套文档的键上建立索引,方式与正常的键一样。如果有这样一个集合,如下所示:
db.emp.insert({ "_id":"A001", "name":{ "first":"Carey", "last":"Ickes" }, "age":25 })
现在我要在内嵌文档的first键上建立索引?如下所示
> db.emp.ensureIndex({"name.first":1})
温馨提示:对嵌套文档本身“name”建立索引,与对嵌套文档的某个字段(name.first)建立索引是完全不相同的,对整个文档建立索引,只有在使用文档完整匹配时才会使用到这个索引,例如建立了这样一个索引db.emp.ensureIndex({"name":1}),那么只有使用db.emp.find({"name":{"first":"xxx","last":"xxx"}})这种完整匹配时才会使用到这个索引,使用db.emp.find({"name.first":"xxx"})是不会使用到该索引的。
三、索引数组
MongoDB支持对数组建立索引,这样就可以高效的搜索数组中的特定元素。例如有一个博客文章集合,其中每个文档都表示一篇文章。每篇文章都有一个comments字段,这是一个数组,用来保存别人对这篇文章的评论信息。blog集合结构如下:
db.blog.insert({ "_id":"B001", "title":"MongoDB查询", "comments":[ {"name":"ickes","score":3,"comment":"nice"}, {"name":"xl","score":4,"comment":"nice"}, {"name":"eksliang","score":5,"comment":"nice"}, {"name":"ickes","score":6,"comment":"nice"} ] })
如果要找出comments.score大于5的所有文章,可以在comments.score键上面建立索引:
> db.blog.ensureIndex({"comments.score":1})
对数组建立索引需要注意
- 对数组建立索引,实际上是对数组的每一个元素建立一个索引条目,如果一篇文章有20个评论,那么它就拥有20个索引条目。因此索引的代价比较单值索引的代价高。
- 在数组上建立的索引并不包含任何位置信息:无法使用数组索引查找特定位置的数组元素,比如"comments.4".
- 一个索引中数组字段最多只能有一个。这是为了避免在复合索引中索引条目爆炸性增长。
- 对于某个索引的键,如果这个键在某个文档中是一个数组,那么这个索引就会标记为多键索引。可以从explain()的输出信息中看到一个索引是否为多键索引。如果是多键索引,那么"isMultikey"字段的值就会为true。索引只要被标记为多键索引,就无法再变成非多键索引,即使索引的键从文档中删除也不行。唯一的办法就是删除重建。
四、explain()和hint()
explian()能够提供大量与查询相关的信息。对于速度比较慢的查询来说,这是最重要的诊断工具。通过查看explian()的输出信息,可以知道查询使用了那个索引,以及如果使用他的。对于任意查询来,都可以在最后添加一个explain()调用,但是调用explain()的时间必须是最后。
参考实例:
> db.users.find({"age":{"$gt":10}}).sort({"age":1}).limit(100).explain() { "cursor" : "BtreeCursor age_1_name_1", "isMultiKey" : false, "n" : 100, "nscannedObjects" : 100, "nscanned" : 100, "nscannedObjectsAllPlans" : 100, "nscannedAllPlans" : 100, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "age" : [ [ 10, Infinity ] ], "name" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ] }, "server" : "localhost.localdomain:27017", "filterSet" : false }
explain()输出各个信息的含义如下表所示
"cursor" : "BtreeCursor age_1_name_1" | 表示本次查询使用了索引,具体使用了age_1_name_1这个索引 |
"isMultiKey" : false | 用于说明本次查询是否使用了多键索引 |
"n" : 100 | 本次查询返回的文档数量 |
"nscannedObjects" : 100 | 这是MongoDB使用索引指针去磁盘上查找文档的实际次数 |
"nscanned" : 100 | 如果有使用索引,那么这个数字就是检查过的索引个数,如果本次查询是全表扫描,那么这个数字就是检查过的文档个数 |
"scanAndOrder" : false | MongoDB是否在内存中对结果集进行了排序 |
"indexOnly" : false | MongoDB是否只使用索引就可以完成本次查询("覆盖索引") |
"nYields" : 0 | 为了让写入请求能够顺利执行,本次查询暂停的次数。如果有写入请求需要处理,查询会周期性地释放他的锁,以便写人能够顺利执行。然而,在本次查询中没有写入请求,因为查询没有暂停过。 |
"millis" : 0 | 数据库执行本次查询所耗费的毫秒数。这个数字越小,说明查询效率越高 |
indexBounds:{...} | 这个字段描述了索引的使用情况,给出了索引的遍历范围。因为本次查询只使用了age做为查询条件跟排序条件,没有指定第二个键,因此在name键上没有限制,数据库会把用户名介于负无穷("$minElement" : 1)到正无穷("$maxElement" : 1)之间进行搜索 |
hint()方法用于让MongoDB强制使用某个索引,或者查询使用了索引反而效率更低,这个时候可以使用hint()屏蔽索引让查询走全表扫描。
参考实例:强制本次查询使用{"name":1,"age":1}这个索引
> db.users.find({"age":{"$gt":10}}).sort({"age":1}).hint({"name":1,"age":1}).explain()
参考实例:强制本次查询走全表扫描
> db.users.find({"age":{"$gt":10}}).sort({"age":1}).hint({"$natural":true}).explain()--使用{"$natural":1}--$natural 强制全表扫描
五、MongoDB中低效率的操作符
"$where"和"$exists":这两个操作符,完全不能使用索引。
"$ne":通常来说取反的效率比较低。"$ne"查询可以使用索引,但并不是很有效。因为他必须查看所有的索引条目,而不是"$ne"指定的条目,这个时候他就不得不扫描整个索引。
"$not":有时候能够使用索引,但是他通常并不知道要如何使用索引。所以大多数情况"$not"会退化为全表扫描。
"$nin":这个操作符总是会全表扫描
六、OR查询
MongoDB在一次查询中只能使用一个索引(至少我现在用的2.6是这样的),如果你在{"x":1}上有一个索引,在{"y":1}上也有一个索引,在{"x":1,"y":1}上执行查询时,MongoDB只会使用其中一个索引,而不是两个一起使用。"$or"是一个例外,"$or"可以对每个字句都使用索引,因为"$or"实际上是执行两次查询然后将结果合并。
通常来说,使用or查询多次在合并结果,不如单次查询的效率高,对于单个字段,应该尽可能使用$in。
七、MongoDB的查询优化器
MongoDB的查询优化器与其他数据库的稍微不同。基本来说,如果一个索引能够精确匹配一个查询,那么查询优化器就会使用这个索引,如果不能精确匹配,可能会有几个索引都适合你的查询。那MongoDB是怎样选择的呢?答:MongoDB的查询计划会将多个索引并行的去执行,最早返回100个结果的就是胜者,其他查询计划都会被终止。
这个查询计划会被缓冲,接下来的这个查询都会使用他,下面几种情况会重新计划;
- 最初的计划评估之后集合发生了比较大的数据波动,查询优化器就会重新挑选可行的查询计划。
- 建立索引时。
- 每执行1000次查询之后,查询优化器就会重新评估查询计划
八、何时不应该使用索引
提取较小的子数据集时,索引非常有效(所以才有了分页)。也有一些查询不使用索引会更快。结果集在原集合中所占的比例越大,查询效率越慢。因为使用索引需要进行两次查找:一次查找索引条目,一次根据索引指针去查找相应的文档。而全表扫描只需要进行一次查询。在最坏的情况,使用索引进行查找次数会是全表扫描的两倍。效率会明显比全表扫描低。
可惜并没有一个严格的规则可以告诉我们,如果根据索引大小、文档大小来判断什么时候索引很有用,一般来说,如果查询需要返回集合内30%的文档(或者更多),那就应该测试全表扫描和走索引查询那个速度比较快。这个数字也会在2%~60%之间进行波动。
这个时候可以使用hint({"$natural":true})强制查询走全表扫描。
相关推荐
15、MongoDB建模调优&change stream实战_ev.rar15、MongoDB建模调优&change stream实战_ev.rar15、MongoDB建模调优&change stream实战_ev.rar15、MongoDB建模调优&change stream实战_ev.rar15、MongoDB建模调优&...
难得见到的MongoDB性能调优文档,文章基于MongoDB的Ops Manager展示指标来分析MongoDB性能问题。
NOSQL应用:文档MongoDB在网站开发中应用越来越广泛,本文描述了如何对MongoDB进行性能调优。原创内容。
常见NoSQLj介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB 常见NoSQL介绍——MongoDB...
适用于对mongodb有一定的使用经验,并且希望更深了解的人群,对mongodb的索引,执行计划有详细的介绍
《左手MongoDB,右手Redis——从入门到商业实战》背后的故事。 这篇文章没有代码,请放心阅读。 一个程序员的一生应该这样度过:当她回首往事的时候,她不会因为建造环境浪费时间而悔恨,也不会因为浪费而无法这样,...
高性能可扩展mysql Mysql视频教程 mongodb视频教程 oracle视频教程, 主要讲解了以上三种当前主流数据库的高级知识,侧重于数据库调优的高级技能,对于开发高性能/并发网站的学习朋友应该有所帮助。
MongoDB数据库索引介绍.pptx
MongoDB索引优化与管理-刘诚杰 天痕,介绍 1. 索引概念 2. 索引优化 3. 执行计划 4. 索引管理
MongoDB索引管理与高级索引.pdf 学习资料 复习资料 教学资源
万亿级文档数据库MongoDB集群性能优化实践.pdf
MongoDB索引几乎和关系型数据库的索引一样.MongoDB的查询优化器能够使用这种数据结构来快速的对集合(collection)中的文档(collection)进行寻找和排序.准确来说,这些索引是通过B-Tree索引来实现的。在命令行中,可以...
MongoDB索引限制.pdf 学习资料 复习资料 教学资源
MongoDB索引管理.pdf 学习资料 复习资料 教学资源
MONGO的索引跟MYSQL、ORACLE基本上差不多,实现了其中的一部分,当然了,肯定是没有它们功能齐全的,因为MONGO本质上也是基于磁盘文件的,所以如果索引的话,效率是不敢想像的!
MongoDB索引与查询.pdf 学习资料 复习资料 教学资源
window_mongodb执行过程个人笔记——不建议下载.txt Mongodb,分布式文档存储数据库,由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个高性能,开源,无模式的文档型数据库,是当前...
MongoDB索引的创建docx.pdf 学习资料 复习资料 教学资源
mongodb中如何建立高效索引,文档讲述的很清楚
1.分篇章进行学习,内容控制30分钟内 2.1个月疗程,不要放弃治疗哦 3.图文并茂,有问题请发到邮箱