MongoDB 索引优化

索引是用来加快查询速度的,事物都有双面性的,同时在每次插入、更新和删除操作时都会产生额外的开销。索引有时并不能解决查询慢的问题,一般来说,返回集合中一半以上的结果,全表扫描要比查询索引更高效些。创建太多索引,会导致插入非常慢,同时还会占用很大空间。可以通过一些工具来分析查询的效率来进一步优化索引。
一、MongoDB自带工具explain
使用explain命令返回查询使用的索引情况,耗时,扫描文档数等等统计信息。 
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. > db.user.find({name:/^zhangsan2*2$/,index:{"$lt":299190}}).explain();   
  2. {   
  3. "cursor" : "BasicCursor",   
  4. "isMultiKey" : false,   
  5. "n" : 6,   
  6. "nscannedObjects" : 2000000,   
  7. "nscanned" : 2000000,   
  8. "nscannedObjectsAllPlans" : 2000000,   
  9. "nscannedAllPlans" : 2000000,   
  10. "scanAndOrder" : false,   
  11. "indexOnly" : false,   
  12. "nYields" : 1,   
  13. "nChunkSkips" : 0,   
  14. "millis" : 1240,   
  15. "indexBounds" : {   
  16.   
  17. },   
  18. "server" : "100.205:27017"   
  19. }   
字段说明: 
cursor:返回游标类型 
isMultiKey:是否使用组合索引 
n:返回文档数量 
nscannedObjects:被扫描的文档数量 
nscanned:被检查的文档或索引条目数量 
scanAndOrder:是否在内存中排序 
indexOnly: 
nYields:该查询为了等待写操作执行等待的读锁的次数 
nChunkSkips: 
millis:耗时(毫秒) 
indexBounds:所使用的索引 
server: 服务器主机名 
可以结合hint强制使用索引来分析。 

二、开启profiling功能,设置日志级别,对日志进行分析 
1.查看profiling级别: 
>db.getProfilingLevel() 
2.设置profiling级别: 
语法:db.setProfilingLevel(level,slowms) 
level - profile的级别可以取0,1,2 表示的意义如下: 
#0 - 关闭性能分析,测试环境可以打开,生成环境关闭,对性能有很大影响; 
#1 - 开启慢查询日志,执行时间大于100毫秒的语句 
#2 - 开启所有操作日志 
slowms - 慢查询时间阀值,默认100ms 
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. >db.setProfilingLevel(1,100)   
  2. { "was" : 0, "slowms" : 100, "ok" : 1 }   
  3. >db.setProfilingLevel(0)   
3.查询profiling记录 
MongoDB Profile 记录是直接存在系统 db 里的,记录位置system.profile 。 
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. > db.system.profile.find().sort({$natural:-1}).limit(1)   
  2. {  
  3.  "ts": ISODate("2015-03-19T08:42:51.012Z"),  
  4.  "op": "query",  
  5.  "ns": "test.system.profile",  
  6.  "query": {  
  7.   "query": {  
  8.      
  9.   },  
  10.   "orderby": {  
  11.    "$natural": -1  
  12.   }  
  13.  },  
  14.  "ntoreturn": 1,  
  15.  "ntoskip": 0,  
  16.  "nscanned": 1,  
  17.  "keyUpdates": 0,  
  18.  "numYield": 0,  
  19.  "lockStats": {  
  20.   "timeLockedMicros": {  
  21.    "r": NumberLong(118),  
  22.    "w": NumberLong(0)  
  23.   },  
  24.   "timeAcquiringMicros": {  
  25.    "r": NumberLong(6),  
  26.    "w": NumberLong(4)  
  27.   }  
  28.  },  
  29.  "nreturned": 1,  
  30.  "responseLength": 385,  
  31.  "millis": 0,  
  32.  "client": "127.0.0.1",  
  33.  "user": ""  
  34. }  
参数介绍:
ts:操作执行时的时间戳
millis:执行操作所花的时间
query:数据库查询操作,查询字段信息包括ntoreturn,query,nscanned,reslen,nreturned
ntoreturn:从查询中返回客户端指定的对象数
query:查询操作信息
nscanned:在执行查询操作的时候扫描了多少对象
reslen:查询结果的大小
nreturned:从查询中返回的结果对象
update:数据库更新操作,
insert:数据库插入操作
getmore:大数据量查询
查询优化:
1、如果nscanned 比 nreturned 大很多时,说明数据库扫描了很大对象才找到目标对象,因此需要为条件查询创建索引
2、当返回的结果集很大时即reslen值相当大时,会影响性能下降,在做find查询时,需要添加第二个查询参数,只获取需要显示的字段
参考: http://www.cnblogs.com/DxSoft/archive/2010/10/21/1857357.html
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. > db.system.profile.find().pretty().limit(1)   
  2. {   
  3. "ts" : ISODate("2015-03-19T08:36:13.451Z"),   
  4. "op" : "command",   
  5. "ns" : "test.$cmd",   
  6. "command" : {   
  7. "profile" : 2,   
  8. "slowms" : 100   
  9. },   
  10. "ntoreturn" : 1,   
  11. "keyUpdates" : 0,   
  12. "numYield" : 0,   
  13. "lockStats" : {   
  14. "timeLockedMicros" : {   
  15. "r" : NumberLong(0),   
  16. "w" : NumberLong(18)   
  17. },   
  18. "timeAcquiringMicros" : {   
  19. "r" : NumberLong(0),   
  20. "w" : NumberLong(7)   
  21. }   
  22. },   
  23. "responseLength" : 58,   
  24. "millis" : 0,   
  25. "client" : "127.0.0.1",   
  26. "user" : ""   
  27. }   
字段说明: 
ts: 该命令在何时执行 
op: 操作类型 
query: 本命令的详细信息 
responseLength: 返回结果集的大小 
ntoreturn: 本次查询实际返回的结果集 
millis: 该命令执行耗时,以毫秒记 
4.修改profiling大小 
capped Collections 比普通Collections 的读写效率高。Capped Collections 是高效率的Collection类型,它有如下特点: 
a. 固定大小;Capped Collections 必须事先创建,并设置大小: 
> db.createCollection("collection", {capped:true, size:100000}) 
b. Capped Collections 可以insert 和update 操作,不能delete 操作。只能用drop()方法删除整个Collection。 
c. 默认基于Insert 的次序排序的。如果查询时没有排序,则总是按照insert 的顺序返回。 
d. FIFO。如果超过了Collection 的限定大小,则用FIFO 算法,新记录将替代最先insert的记录 
[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. > db.setProfilingLevel(0)   
  2. { "was" : 2, "slowms" : 100, "ok" : 1 }   
  3. > db.getProfilingLevel()   
  4. 0   
  5. > db.system.profile.drop()   
  6. true   
  7. > db.createCollection("system.profile",{capped:true, size: 1000000})   
  8. { "ok" : 1 }   
  9. > db.system.profile.stats()   
  10. {   
  11. "ns" : "test.system.profile",   
  12. "count" : 0,   
  13. "size" : 0,   
  14. "storageSize" : 1003520,   
  15. "numExtents" : 1,   
  16. "nindexes" : 0,   
  17. "lastExtentSize" : 1003520,   
  18. "paddingFactor" : 1,   
  19. "systemFlags" : 0,   
  20. "userFlags" : 0,   
  21. "totalIndexSize" : 0,   
  22. "indexSizes" : {   
  23.   
  24. },   
  25. "capped" : true,   
  26. "max" : 2147483647,   
  27. "ok" : 1   
  28. }   
关于capped Collections的介绍,请参考: http://blog.csdn.net/zhu_tianwei/article/details/44422995 

三、查询分析器—dex 
mongodb索引和查询分析器dex,是一种MongoDB的性能调整工具,比较MongoDB的日志文件和索引条目并给出索引建议。目前,必须提供一个连接数据库的URI。dex只建议完整的索引,而不是部分索引。不支持Windows平台。dex在运行过程中主要会进行下面三个步骤: 
1.解析query 
2.通过已存在的索引对当前query进行判断 
3.如果发现索引不当,就推荐合适的索引。 

第一步:解析query 
Dex会对查询query进行解析,分成下面几大类 
EQUIV – 普通按数值进行的查询,比如:{a: 1} 
SORT – sort操作,比如: .sort({a: 1}) 
RANGE – 范围查询,比如:Specifically: ‘$ne’, ‘$gt’, ‘$lt’, ‘$gte’, ‘$lte’, ‘$in’, ‘$nin’, ‘$all’, ‘$not’ 
UNSUPPORTED 
组合式查询,比如:$and, $or, $nor 
除了RANGE之外的嵌套查询 

第二步:判断当前索引情况 
有两个标准来找出查询所需的索引。 
Coverage (none, partial, full) - Coverage表示索引的情况,有括号中的三个值。none表示完全无索引覆盖。full表示query中的字段都能找到索引。partial表示none和full之间的情况。 
Order (ideal or not) - Order是用于判断索引的顺序是否理想。理想的索引顺序应该是: Equivalence ○ Sort ○ Range 值得注意的是,对地理位置索引只会进行分析,但是不会提出改进建议。 

第三步:推荐合适的索引 
通过上面两步,我们能够对一个查询可能使用索引的情况有一个了解。Dex会生成一个此查询的最佳索引。如果这个索引不存在,并且查询情况不包括上面提到的UNSUPPORTED,那么Dex就会做出相应的索引优化建议。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值