【四】MongoDB索引管理
一、索引介绍
在mongodb中,索引用来支持高效查询。如果没有索引,mongodb必须在整个集合中扫描每个文档来查找匹配的文档。但是如果建立合适的索引,mongodb就可以通过索引来限制检查的文档数量。
索引是一种特殊的数据结构,它存储着集合中小部分的数据集,这种数据结构很容易遍历。索引存储着指定的字段或字段集合,这些字段都是根据字段值排序的。排序的索引条目能够支持高效的等值匹配和基于范围的查询操作,此外,mongodb通过排序索引还能够返回排好序的结果集。
从根本上来说,mongodb的索引类似于其他关系型数据库的索引,它被定义在集合层面并支持任何字段或子域,它采用B-tree数据结构。
二、索引概念
1、索引类型
MongoDB提供多种不同类型的索引。对于某一文档或者内嵌文档,你能够在任意字段或者内嵌字段上创建索引。一般而言,你应该创建通用的面向用户的索引。通过这些索引,确保mongodb扫描最少最有可能匹配的文档。在mongodb的shell中,你能通过调用createIndex()方法创建一个索引。
1)单字段索引
对于集合中的文档,mongodb完全支持在任何字段上创建索引。默认地,任何集合的_id字段上都有一个索引,并且应用和用户还可以添加额外的索引来支持重要的查询和操作。mongodb既支持单字段索引也支持多个字段的复合索引,这里先介绍单字段索引,下面请看举例说明:
> db.friends.insert({"name" :"Alice","age":27}) #集合friends中的一个文档
WriteResult({ "nInserted" : 1 })
> db.friends.createIndex({"name" :1}) #在文档的name字段上建索引
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
db.collection.createIndex(keys,options)介绍:
| Parameter | Type | Description |
|---|---|---|
| keys | document |
A document that contains the field and value pairs where the field is the index key and the value describes the type of index for that field. For an ascending index on a field, specify a value of 1; for descending index, specify a value of -1. MongoDB supports several different index types including GeoJSON Point的位置数据文档,如下: db.places.insert( { loc : { type: "Point", coordinates: [ -73.97, 40.77 ] }, name: "Central Park", category : "Parks" } ) db.places.insert( { loc : { type: "Point", coordinates: [ -73.88, 40.78 ] }, name: "La Guardia Airport", category : "Airport" } ) 然后,新建一个基于loc字段的2dsphere索引: db.places.createIndex( { loc : "2dsphere" } )
当然,还可以创建包含2dsphere索引的复合索引: db.places.createIndex( { loc : "2dsphere" , category : -1, name: 1 } )
db.places.createIndex( { category : 1 , loc : "2dsphere" } ) #不像2d索引,这里不要求2dsphere类型在第一个位置
【注意事项】:2dsphere索引的字段必须是基于坐标对和GeoJSON数据格式。
该索引类型用于数据作为点存储在二维平面情景下,一般用在v2.2版本之前的基于坐标对数据格式的,这里不详细介绍。
geoHaystack索引是一种特别的索引,一般被优化用来返回小区域的结果集。当用平面几何体存储数据形式时,用geoHaystack可以提高查询性能。而对于使用曲面几何体,2dsphere索引将是更好的选择,它允许字段重新排序,而geoHaystack要求第一个字段必须是位置字段。具体这里不详细介绍。
5)散列索引 散列索引维护着索引字段的散列值条目,散列函数能够折叠内置文档并计算整个值的散列数,它不支持多键索引。 MongoDB的散列索引支持等值查询,不支持基于范围的查询。你不能够创建一个包含散列索引字段的复合索引,也不能指定一个唯一索引在散列索引上,但是你可以在同一个字段上创建散列索引和单字段索引。 下面是创建散列索引的例子: db.collection.createIndex( { _id: "hashed" } )
6)全文索引 MongoDB提供全文索引来支持文本字符串的查询效率,它可以建立在任何是字符串类型或元素为字符串数组的字段上。一个集合最多可以有一个全文索引。
db.reviews.createIndex( { comments: "text" } )
当然你也可以创建一个包括多个字段的全文索引,也就是复合索引可以包括全文索引: db.reviews.createIndex( { subject: "text", comments: "text" } ) 【指定权重】:权重是指全文索引字段之间的比率,比如下面的content:10,keywords:5,表示content在查询中出现2次,keywords才出现1次。 db.blog.createIndex( { content: "text", keywords: "text", about: "text" }, { weights: { content: 10, keywords: 5 }, name: "TextIndex" } ) 【通配符】:创建全文索引时还可以利用通配符: db.collection.createIndex( { "$**": "text" } )
db.collection.createIndex( { a: 1, "$**": "text" } )
用这种方式创建的全文索引,只要是集合中存在字符串类型的字段全部加入到全文索引中,这种常常用在非结构化数据并且不确定字段的情况下。
集合最多有一个全文索引
如果查询中包含$test表达式,不能用hint()函数
不支持排序操作
如果全文索引包含在复合索引中,那么,这个复合索引不能包括多键和空间索引
2、索引属性 MongoDB中除了支持上面索引类型外,还提供了一些常用的索引属性。 1)TTL indexes TTL索引是一类特殊的单字段索引,用来自动的删除集合中过期的数据。数据期限对于像机器码生成、日志及session等数据是很有用的。
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } ) #通过加上expireAfterSeconds定义TTL索引
从索引字段值开始,过去了指定的秒数之后,TTL索引使文档过期。而过期的阈值等于索引值加上指定的秒数。如果索引字段是数组类型的,那么在TTL索引上会存在多个时间值,mongodb会采用最早的时间值进行阈值计算。如果索引字段不是date类型的,那么文档永不过期;如果文档不包含TTL索引,那么文档也永不过期。 mongodb会启动后台TTL线程每隔60秒读取索引的值并且删除过期的数据,当TTL线程的状态是active,你可以通过db.currentOp()查看到删除操作。TTL索引并不保证当数据过期时立刻被删除,可能会有一段时间的延迟。
1、TTL索引是单字段索引,不支持组合索引。
2、_id字段不支持TTL索引
3、在capped集合中不支持TTL索引
4、对已存在的TTL索引,你不能通过createIndex方法修改expireAfterSeconds的值,而是通过collMod命令连同索引集合标志。要不然,只能删掉重建。
5、对于原来已存在的非TTL的单域索引,你不能再在该字段上建TTL索引,为了把非TTL索引转为TTL索引,这时你必须删除原索引重建。
2)Unique indexes(唯一索引) 对于加了唯一索引的字段,mongodb将会拒绝插入该字段重复值的所有文档。默认创建索引时,唯一索引参数时禁用的。
db.members.createIndex( { "user_id": 1 }, { unique: true } )
对于组合索引,如果unique是true的话,那么唯一性由这些字段的组合值确定。 如果唯一索引字段是数组或内置文档类型的,唯一索引并不保证里面的值是否唯一,如下面例子: db.collection.createIndex( { "a.b": 1 }, { unique: true } )
db.collection.insert( { a: [ { b: 5 }, { b: 5 } ] } ) #这时完全可以插入
如果对于唯一索引字段没有值,那么默认存储null值。由于唯一性约束,mongodb仅仅允许一次不包括该索引字段的插入,如果大于1次,那么就会报错。举例说明: 首先在x字段上创建唯一索引: db.collection.createIndex( { "x": 1 }, { unique: true } )
其次,执行不带x字段的插入语句: db.collection.insert( { y: 1 } ) #这时可以插入,因为在插入之前集合中还不包括值为null的x
然后,再执行一次不包含x的插入语句: db.collection.insert( { z: 1 } ) #这时报错,由于之前插入了值为null的x
3)Partial Indexes(局部索引) 局部索引仅仅是为集合中某些满足指定过滤条件的文档建立的索引。通过对集合中部分文档建立索引,故局部索引对存储、索引创建和维护性能成本有较低的要求。
db.restaurants.createIndex( { cuisine: 1, name: 1 }, { partialFilterExpression: { rating: { $gt: 5 } } } ) 可选参数partialFilterExpression适用于所有的索引类型。
1 查询谓词必须包含过滤表达式
2 查询条件必须是局部索引结果集的本身或子集
对于上面的索引,下面举例几个查询,通过利用条件,看是否能用上该局部索引: 1、db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } ) #可以走局部索引,因为查询表达式的结果集是局部索引结果集的子集
2、db.restaurants.find( { cuisine: "Italian" } ) #无法利用局部索引,因为不满足条件1:查询谓词中没有过滤表达式rating
3、db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } ) #无法利用局部索引,因为走索引会导致不完整的结果集
对于带唯一性约束的局部索引,这个唯一性约束只在满足局部索引的范围文档中有效,而对于不走局部索引的,唯一性约束不起作用。 { "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }
{ "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }
{ "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }
db.users.createIndex(
{ username: 1 },
{ unique: true, partialFilterExpression: { age: { $gte: 21 } } }
)
#以下三个唯一性约束可以有作用
db.users.insert( { username: "david", age: 27 } )
db.users.insert( { username: "amanda", age: 25 } )
db.users.insert( { username: "rajiv", age: 32 } )
#以下唯一性不起作用
db.users.insert( { username: "david", age: 20 } )
db.users.insert( { username: "amanda" } )
db.users.insert( { username: "rajiv", age: null } )
4)Sparse Indexes(稀疏索引) 所谓稀疏索引就是仅包含有索引字段的文档条目,即使字段的值为null。由于该索引会跳过没有索引字段的文档,故取名“稀疏索引”。稀疏索引不包括所有集合中的文档,相反的,非稀疏索引则包括集合中所有文档。 在mongodb3.2及以后的版本中,推荐优先使用部分索引。
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
如果使用稀疏索引导致得到不完整的结果集,那么mongodb将不用改索引除非显式地用hint()函数指定使用。下面是使用例子: { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
db.scores.createIndex( { score: 1 } , { sparse: true } )
db.scores.find( { score: { $lt: 90 } } ) #由于userid=newbie没有score字段,这样就不满足稀疏索引的条件,故只返回下面一个文档
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
对于上面的集合,再来看个排序: db.scores.find().sort( { score: -1 } ) #尽管该排序是在score索引字段上,但是mongodb不会选择稀疏索引,这样就可以返回完整的结果集
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }
#为了指定使用稀疏索引,必须显式用hint方法
db.scores.find().sort( { score: -1 } ).hint( { score: 1 } )
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
对于带唯一约束的稀疏索引: 唯一性约束只能在满足稀疏索引的文档上有作用,在其他文档上都没作用,如下: { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
db.scores.createIndex( { score: 1 } , { sparse: true, unique: true } )
#下面四个可以进行插入
db.scores.insert( { "userid": "AAAAAAA", "score": 43 } )
db.scores.insert( { "userid": "BBBBBBB", "score": 34 } )
db.scores.insert( { "userid": "CCCCCCC" } )
db.scores.insert( { "userid": "DDDDDDD" } )
#下面违反唯一性约束
db.scores.insert( { "userid": "AAAAAAA", "score": 82 } )
db.scores.insert( { "userid": "BBBBBBB", "score": 90 } )
相关 |