Mongodb索引及查询优化分析
创建索引
db.collection.createIndex(keys, options)
- 参数说明:
- keys: {FieldNameOne:ascending,… FieldNameN:ascending}: ascending 设为1 标识索引升序,-1降序
- options : 设置索引选项,如设置名称、设置成为唯一索引
唯一(_id)索引
MongoDB都会自动生成一条唯一的_id字段
唯一索引会阻止应用插入被索引键上的值是重复值的documents
-
不能再已经有重复数据的字段上建立唯一索引
db.collections.createIndex({“FieldName”: 1或-1},{“unique”:true})
单键索引
-
在一个键上创建的索引就是单键索引
db.collection.createIndex({'fieldName':1/-1});
复合索引
查询多个条件时,建立复合索引
-
在多字段上建唯一索引会强制要求复合键值的唯一性,而不是每个键的唯一性
db.collection.createIndex({'fieldName_one':1/-1,'fieldName_n..':1/-1});
-
总结:
- 查询条件只是复合索引key中部分字段时,如果想要复合索引起到优化的作用则必须包含创建复合索引时指定的第1个字段;
- 可以指定在索引的所有键或者部分键上排序。但是,排序键的顺序必须和它们在索引中的排列顺序一致sort中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序完全相同, 或者 完全相反。
多键索引
-
多键索引与单键索引创建形式相同,区别在于字段的值,如:数组
db.collection.createIndex({'arrayFieldName':1/-1});
稀疏索引
db.collection.createIndex({“FieldName”:1},{“sparse”:true})
-
在一个字段上创建稀疏索引,索引会跳过所有不包含被索引键(字段)的文档在执行查询 { 被索引键:{$exists:false}},不会使用该稀疏索引,除非显示指定hint({被索引键:1})
db.scores.createIndex( { score: 1 } , {sparse:true} ) db.scores.find( { score:{$exists:false}}).hint({score:1}).explain()
-
总结
- 在某一个被创建了稀疏索引字段上执行exists:false查询时,需要显示指定hint,其索引才会起作用;而执行 exists:true查询时,则不需要。
- 在字段上创建普通索引,如果文档不含该字段这其索引值会被设为null,而稀疏索引会跳过该文档;这就是说使用该索引扫描集合时稀疏索引会比普通索引少。
局部索引(Partial Index)
db.collection.createIndex(
{“FieldName”: 1/-1,…”FieldNameN”: 1/-1},
{“partialFilterExpression”:{partialFilterExpression }})
db.contacts.createIndex(
{ name: 1 },
{ partialFilterExpression: { name: { $exists: true } } }
)
- $eq
- $gt, $gte, $lt, $lte
- $exists: true
TTL(过期)索引
在一段时间后会过期的索引,在索引过期后,相应的数据会被删除,适合存储在一段时间之后会失效的数据;
存储在过期索引字段的值必须是指定的时间类型,必须是ISODate或者ISODate数组,不能使用时间戳,否则不能自动删除;
如果指定了ISODate数组,则按照最小的时间进行删除;
过期索引不能是复合索引,因为不能指定两个过期时间;
-
删除时间是不精确的:删除过程是由MongoDB的后台进程每60s跑一次的,而且删除也需要一定时间,所以存在误差。
db.collection.createIndex({'dateTimeField':1},{expireAfterSeconds:100(秒)});
全文索引
对字符串与字符串数组创建全文课搜索的索引;
在MongoDB中每个数据集合只允许创建一个全文索引,不过这个全文索引可以针对一个、多个、全部的数据集合的字段来创建;
每次查询只能指定一个$text查询$text查询不能出现在$nor查询中;
-
查询中如果包含了$text, hint不再起作用
db.file.find({$text:{$search:'mongo'},'status.pub':true},{ score: { $meta: "textScore" } }).sort( {score: { $meta: "textScore" }} )
地理空间索引
2D索引,用于存储和查找平面上的点
-
2Dsphere索引,用于存储和查找球面上的点
db.collection.createIndex({w:"2d"})
使用$near 查询距离某个点最近的点 ,默认返回最近的100个点
-
可以使用$maxDistance:x 限制返回的最远距离
db.collection.find({w:{$near:[x,y]}}) db.collection.find({w:{$near:[x,y],$maxDistance:z}})
使用$geoWithin 查询某个形状内的点
哈希索引
查询性能分析
db.collection.find({}).explain("queryPlanner"/"executionStats"/"allPlansExecution")
db.file.createIndex({'tag.self':1})
db.file.find({'tag.self':{$in:['node']}}).explain("executionStats")
性能指标
- winningPlan.stage = FETCH(根据索引去检索指定document )
- winningPlan.inputStage.stage = IXSCAN(索引扫描)
- totalKeysExamined 索引扫描条目
- totalDocsExamined 文档扫描条目
- nReturned 查询返回的条目
总结:
- 排序操作可以通过从索引中按照索引顺序获取文档的方式来保证结果的有序性。如果查询计划器(planner)无法从索引中得到排序顺序,那么它将需要在内存中排序(winningPlan.stage=SORT)结果。
- 在多个字段上做排序时需要使用复合索引