MongoDB之三:查询优化

前提:测试数据库有100万条数据,分别存放{"user":"user: 0-1000000","age:"0-100","createAt":new Date()}字段

  • 基于多个字段查询时,尽量将会用于精确查询的字段放在索引前面,范围查询字段放在索引后面。

例如: 查询小于"user: 1080"的user字段并且年龄是40岁的人。

db.foo.find({"user":{"$lt":"user: 1080"},"age":40}).hint({"age":1,"user":1})

×

db.foo.find({"user":{"$lt":"user: 1080"},"age":40}).hint({"user":1,"age":1})
  • 单、多个索引时,多字段排序应该将索引字段放置第一位,排序字段放在末尾。

例如:根据"user"字段升序,"age"字段升序:
(单字段索引)

db.foo.find().sort({"user":1,"age":1}).hint({"user":1})

×

db.foo.find().sort({"user":1,"age":1}).hint({"age":1})

(多字段索引)

db.foo.find({"age":21}).sort({"user":-1}).hint({"age":1,"user":1})

×

db.foo.find({"age":21}).sort({"user":-1}).hint({"user":1,"age":1})
  • MongoDB会对已经添加索引的字段根据你的指示进行排序,查找单个值,根据第二个字段排序不会造成性能影响。

例如:查找年龄为21岁的用户并且根据用户倒序排列:

db.foo.find({"age":21}).sort({"user":-1}).hint({"age":1,"user":1})

该索引里已经是有序的了,即便是倒序,MongoDB也会根据age字段搜索出复合条件的条目然后再逆遍历输出。
PS:db.collection.ensureIndex({"age":1,"user":1})MongoDB索引会先根据age进行升序,然后age的相同条目再会由user进行升序排序。
(-1则是倒序排序)。

  • 根据范围且要排序的查询:
    例如:查询年龄介于15-30之间并且根据user降序处理:

db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).hint({"age":1,"user":1})//该方法的排序操作在内存中进行。

×

db.foo.find({"age":{"$gt":15,"$ lt":30}}).sort({"user":-1}).hint({"user":1,"age":1})//该方法的排序操作不用在内存中进行。

不在内存中进行排序会慢于将排序放于内存中进行。在内存中进行大量数据排序会牺牲性能,无可厚非,但是如果数据过多(超过32MB),则MongoDB会出错,所以建议不要在内存里面进行排序;并且如果限制查询范围,MongoDB在进行几次匹配之后不再匹配索引,在这种情况下,将排序键放在前面是一个非常好的策略。

  • 根据范围且要排序且限制输出的查询:

db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"user":1,"age":1})//该方法的排序操作不在内存中进行。
//executionTimeMillis: 16
//totalKeysExamined: 8693

×

db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"age":1,"user":1})//该方法的排序操作在内存中进行。
//executionTimeMillis: 495
//totalKeysExamined: 231689

一旦限制输出条目,则查询效率跟上一个建议出现反转:
不在内存中进行排序的索引查询时间为16ms,匹配的文档为8693条;
在内存中进行排序的时间为495ms,匹配的文档为231689条。
由此可见,如果限制了返回的条目数,则不在内存中进行排序会快得多。
实际应用中,对需要范围查询的数据进行排序一般会取前面的结果,所以推荐使用{"sortKey":1,"queryCriteria":1}这种索引:
把排序键放在索引的前面键,查询条件放在索引的后面键,并且这种索引排序不会在内存中进行,且限制查询条目的时候非常有优势。
当然,如果是需要获取全部数据,使用{"queryCriteria":1,"sortKey":1},这种方式会比较快那么一丢丢,但是注意,这种方式是在内存中进行排序的,注意32MB限制。
一般情况下,推荐使用{"sortKey":1,"queryCriteria":1}索引。


总结

  • 单索引:匹配精确条件。
  • 多索引:
//有范围条件且需要排序的查询(或许还有输出条目限制),先排序再查询 --> {"sortKey":1,"queryCerteria":1}
db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"user":1,"age":1})
//精确条件且需要排序的查询,先查询后排序 --> {"queryCerteria":1,"sortKey":1}
db.foo.find({"age":21}).sort("user":-1).hint({age:1,user:1})
  • $nin总是进行全表扫描。
  • $or只有在双方的键上都单独创建了索引才会有效率。
  • {a:1,b:1.....z:1}以后,{a:1},{a:1,b:1},{a:1,b:1,c:1}等会自动生效,但是子集{b:1},{c:1},{d:1}或{a:1,c:1}等不会生效。
  • 取反$ne的效率比较低,因为要进行整个索引条目的扫描,因为除了$ne指定的条目,其他条目一样要全部扫描。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文包括以下几个方面: –安全措施 – 部署架构 – 系统优化 – 索引设计 – 备份监控 – 模式设计 – 程序...
    张伟科阅读 9,507评论 0 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,497评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,958评论 18 399
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查询集API 参...
    阳光小镇少爷阅读 9,241评论 0 8
  • 世界太大 我们太小 相遇不易 相知更是万一 不求白首 只求我们相遇在最美的年华 最近在微博游走的时候偶尔发现几张图...
    游弋恶灵阅读 13,290评论 93 394