下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

MongoDB提高查询效率

作者:课课家教育     来源: http://www.kokojia.com点击数:2090发布时间: 2017-08-24 10:00:46

标签: 数据库MongoDB数据库优化

  我们知道,mongodb的索引是B-Tree结构的,和MySQL的索引非常类似。所以你应该听过这样的建议:创建索引的时候要考虑到sort操作,尽量把sort操作要用到的字段放到你的索引后面。但是有的情况下,这样做反而会使你的查询性能更低。

  问题

  比如我们进行下面这样的查询:

  db.collection.find({"country":"A"}).sort({"carsOwned":1})

  查询条件是{“country”:“A”},按carsOwned字段的正序排序。所以索引就很好建了,直接建立country,carsOwned两个字段的联合索引即可。像这样:

  db.collection.ensureIndex({"country":1,"carsOwned":1})

  我们来看一个稍微复杂一点的查询:

  db.collection.find({"country":{"$in":["A","G"]}}).sort({"carsOwned":1})

  这回我们是要查询country为A或者G的数据条目,结果同样按carsOwned字段排序。

  如果我们还使用上面的索引,并且使用explain()分析一下这个查询,就会发现在输出中有一个“scanAndOrder”:true的字段,并且nscanned的值可能会比想象中的大很多,甚至指定了limit也没什么效果。

  原因

  这是什么原因呢,我们先看下面这张图:

左边一个是按{“country”:1,“carsOwned”:1}的顺序建立的索引。而右边是按{“carsOwned”:1,”country”:1}顺序建立的索引。

  如上图所未,左边一个是按{“country”:1,“carsOwned”:1}的顺序建立的索引。而右边是按{“carsOwned”:1,”country”:1}顺序建立的索引。

  如果我们执行上面的查询,通过左边的索引,我们需要将country值为A的(左图的左边一支)所有子节点以及country值为G的(左图的右边一支)所有子节点都取也来。然后再对取出来的这些数据按carsOwned值进行一次排序操作。

  所以说上面explain输出了一个“scanAndOrder”:true的提示,就是说这次查询,是先进行了scan获取到数据,再进行了独立的排序操作的。

  那如果我们使用右边的索引来做查询,结果就不太一样了。我们没有将排序字段放在最后,而是放在了前面,相反把筛选字段放在了后面。那这样的结果就是:我们会从值为1的节点开始遍历(右图的左边一支),当发现有country值为A或G的,就直接放到结果集中。当完成指定数量(指定limit个数)的查找后。我们就可以直接将结果返回了,因为这时候,所有的结果本身就是按carsOwned正序排列的。

  对于上面的数据集,如果我们需要2条结果。我们通过左图的索引需要扫描到4条记录,然后对4条记录进行排序才能返回结果。而右边只需要我们扫描2条结果就能直接返回了(因为查询的过程就是按需要的顺序去遍历索引的)。

  所以,在有范围查询(包括$in,$gt,$lt等等)的时候,其实刻意在后面追加排序索引通常是没有效果的。因为在进行范围查询的过程中,我们得到的结果集本身并不是按追加的这个字段来排的,还需要进行一次额外的排序才行。而在这种情况下,可能反序建立索引(排序字段在前、范围查询字段在后)反而会是一个比较优的选择。当然,是否更优也和具体的数据集有关。

  总结

  总结一下,举两个栗子。

  当查询是:

  db.test.find({a:1,b:2}).sort({c:1})

  那么直接建立{a:1,b:1,c:1}或者{b:1,a:1,c:1}的联合索引即可。

  如果查询是:

  db.test.find({a:1,b:{$in:[1,2]}}).sort({c:1})

  那么可能建立{a:1,c:1,b:1}的联合索引会比较合适。当然,这里只是提供了多一种思路,具体是否采用还是需要视你的数据情况而定。

  小编上面讲了MongoDB范围查询的索引优化,这只是MongoDB查询优化的一部分内容而已,小编下面就跟大家分享MongoDB查询优化原则!这样大家就能更好的使用MongoDB查询~~

  1.在查询条件、排序条件、统计条件的字段上选择创建索引,可以显著提高查询效率。

  2.用$or时把匹配最多结果的条件放在最前面,用$and时把匹配最少结果的条件放在最前面。

  3.使用limit()限定返回结果集的大小,减少数据库服务器的资源消耗,以及网络传输的数据量。

  4.尽量少用$in,而是分解成一个一个的单一查询。尤其是在分片上,$in会让你的查询去每一个分片上查一次,如果实在要用的话,先在每个分片上建索引。

  5.尽量不用模糊匹配查询,用其它精确匹配查询代替,比如$in、$nin。

  6.查询量大、并发大的情况,通过前端加缓存解决。

  7.能不用安全模式的操作就不用安全模式,这样客户端没必要等待数据库返回查询结果以及处理异常,快了一个数量级。

  8.MongoDB的智能查询优化,判断粒度为query条件,而skip和limit都不在其判断之中,当分页查询最后几页时,先用order反向排序。

  9.尽量减少跨分片查询,balance均衡次数少。

  10.只查询要使用的字段,而不查询所有字段。

  11.更新字段的值时,使用$inc比update效率高。

  12.appedcollections比普通collections的读写效率高。

  13.server-sideprocessing类似于SQL查询的存储过程,可以减少网络通讯的开销。

  14.必要时使用hint()强制使用某个索引查询。

  15.如果有自己的主键列,则使用自己的主键列作为id,这样可以节约空间,也不需要创建额外的所以。

  16.使用explain,根据exlpainplan进行优化。

  17.范围查询的时候尽量用$in、$nin代替。

  18.查看数据库查询日志,具体分析的效率低的操作。

  19.mongodb有一个数据库优化工具databaseprofiler,能够检测数据库操作的性能。可以发现query或者write操作中执行效率低的,从而针对这些操作进行优化。

  20.尽量把更多的操作放在客户端,当然这就是mongodb设计的理念之一。

  小编结语:

  更多内容尽在课课家教育!

赞(10)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程