文章目录
索引种类
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录,即会进行全集合扫描
索引主要用于排序和检索
单键索引(常用)
在某一个特定的属性上建立索引,例如:db.users.createIndex({age:-1});
- mongoDB在ID上建立了唯一的单键索引,所以经常会使用id来进行查询;
- 在索引字段上进行精确匹配、排序以及范围查找都会使用此索引;
索引是存储在内存中的,上图说明 {score:1}索引在内存中是按照递增存储的
复合索引(常用)
在多个特定的属性上建立索引,例如:db.users.createIndex({username:1,age:-1,country:1});
上图说明,复合索引 {userid:1,score:-1} 在内存中是按照userid升序、score降序进行存储的,即复合索引在内存中是有顺序的
因此在使用复合索引查询的时候一定要注意顺序,如果查询条件的顺序不一致是不会走索引的
多键索引
在数组的属性上建立索引,例如:db.users. createIndex({favorites.city:1})
针对这个数组的任意值的查询都会定位到这个文档,即多个索引入口或者键值引用同一个文档
哈希索引
不同于传统的B-树索引,哈希索引使用hash函数来创建索引。
例如:db.users. createIndex({username : ‘hashed’})
索引语法
MongoDB使用 ensureIndex() 方法来创建索引
ensureIndex()方法基本语法格式如下所示: db.collection.createIndex(keys, options)
创建索引
- 单键唯一索引:db.users.createIndex({username :1},{unique:true});
- 单键唯一稀疏索引:db.users.createIndex({username :1},{unique:true,sparse:true});
- 复合唯一稀疏索引:db.users.createIndex({username:1,age:-1},{unique:true,sparse:true});
- 创建哈希索引并后台运行:db.users.createIndex({username :‘hashed’},{background:true});
删除索引
慢查询优化
第一步:找出慢查询语句
首先开启内置的查询分析器,记录读写操作效率
db.setProfilingLevel(n,{m}),n的取值可选0,1,2
然后查询监控结果,监控结果保存在一个特殊的盖子集合system.profile里,这个集合分配了128kb的空间,要确保监控分析数据不会消耗太多的系统性资源
盖子集合维护了自然的插入顺序,可以使用 $natural 操作符进行排序,如:db.system.profile.find().sort({’$natural’:-1}).limit(5)
盖子集合system.profile说明:
- 大小或者数量固定;
- 不能做update和delete操作;
- 容量满了以后,按照时间顺序,新文档会覆盖旧文档
第二步:分析慢查询
找出慢速查询的原因比较棘手,原因可能有多个:应用程序设计不合理、不正确的数据模型、硬件配置问题,缺少索引等;接下来对于缺少索引的情况进行分析
使用 explain 分析慢查询,例如:
db.orders.find({"useCode":"james", "orderTime" :
{ "$lt" : new Date("2015-04-03T16:00:00.000Z")}}).explain('executionStats')
explain的入参可选值为:
- “queryPlanner” 是默认值,表示仅仅展示执行计划信息
- “executionStats” 表示展示执行计划信息同时展示被选中的执行计划的执行情况信息,通常使用这个即可
- “allPlansExecution” 表示展示执行计划信息,并展示被选中的执行计划的执行情况信息,还展示备选的执行计划的执行情况信息
展示结果如下:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "maohw.orders",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"useCode" : {
"$eq" : "james"
}
},
{
"orderTime" : {
"$lt" : ISODate("2015-04-03T16:00:00Z")
}
}
]
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"useCode" : {
"$eq" : "james"
}
},
{
"orderTime" : {
"$lt" : ISODate("2015-04-03T16:00:00Z")
}
}
]
},
"direction" : "forward"
},
"rejectedplans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 4064,
"executionTimeMillis" : 161,
"totalKeysexamined" : 0,
"totalDocsexamined" : 500001,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"useCode" : {
"$eq" : "james"
}
},
{
"orderTime" : {
"$lt" : ISODate("2015-04-03T16:00:00Z")
}
}
]
},
"nReturned" : 4064,
"executionTimeMillisEstimate" : 146,
"works" : 500003,
"advanced" : 4064,
"needTime" : 495938,
"needYield" : 0,
"saveState" : 3910,
"restoreState" : 3910,
"iSEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsexamined" : 500001
}
},
"serverInfo" : {
"host" : "localhost.localdomain",
"port" : 27017,
"version" : "3.4.20",
"gitVersion" : "447847d93d6e0a21b018d5df45528e815c7c13d8"
},
"ok" : 1
}
第三步:分析explain结果
explain相关项说明:
queryPlanner(执行计划描述)
winningPlan(被选中的执行计划)
stage(可选项:COLLSCAN 没有走索引,IXSCAN使用了索引)
rejectedplans(候选的执行计划)
executionStats(执行情况描述)
nReturned (返回的文档个数)
executionTimeMillis(执行时间ms)
totalKeysexamined (检查的索引键值个数)
totalDocsexamined (检查的文档个数)
优化目标:
索引优化实战
-
首先构造50w的数据,数据结构:
private String id; private String orderCode; private String useCode; private Date orderTime; private BigDecimal price;
-
测试语句
db.orders.find({"useCode":"james", "orderTime" : {"$lt" : new Date("2015-04-03T16:00:00.000Z")}}).explain('executionStats')
在没有建立索引的情况下,上面这句话查询是非常耗时的,比如600ms
-
新建第一个单键索引
db.orders.createIndex({"useCode":-1});
发现会走useCode索引,查询时间降到160ms
-
新建一个复合索引
db.orders.createIndex({"useCode":-1,"orderTime":-1});
发现走了复合索引,查询时间可以降到18ms
-
最后演示复合索引的使用和索引顺序是有关系的,创建如下复合索引
db.users.createIndex({username:1,age:-1})
则如下的语句是否会走该索引的情况说明:
用了索引:db.users.find().sort({username:1,age:-1}).explain("executionStats") 用了索引:db.users.find().sort({username:-1,age:1}).explain("executionStats") 不用索引:db.users.find().sort({username:-1,age:-1}).explain("executionStats") 不用索引:db.users.find().sort({age:-1,username:1}).explain("executionStats")
说明复合索引中的顺序是极其重要的,只要查询条件和复合索引中的全正向或全反向都可以用到索引,否则不走索引!!!
索引建议
-
索引很有用,但是它也是有成本的——它占内存,让写入变慢
-
复合索引的顺序非常重要!!!
-
索引是用来查询小范围数据的,不适合使用索引的情况
-
每次查询都需要返回大部分数据的文档,避免使用索引
-
写比读多的场景也不适合使用索引
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。