一 索引简介
索引是一种特殊的数据结构,只保存一个集合的小部分数据,方便遍历。它保存了一个指定域的值或者几个域的值,而且他们是排过序的。
在MongoDB数据库中存储的索引数据信息基本上和其他的数据库系统是相同的。MongoDB 在集合层次上来定义索引,可以在集合的任意字段上建立索引,也可以在子文档字段上建立索引。
索引的建立应该是面向用户查询的,使用索引保证只需要查看少量的数据集,使用ensureIndex()来建立索引。
MongoDB中使用B-Tree类管理索引,可以有效的支持范围查询和匹配查询,在集合内部文档可以按照升序或者降序来排列文档,MongoDB可以改变排序的方式(升序或者降序),对于单字段的索引,索引的升序和降序是可以改变的,但是对于复合索引,如果改变升序或者降序将对结果产生很大的影响。
注:MongoDB使用索引返回的文档会自动按照主键进行排序,这样就不用再使用sort进行排序。
如果返回结果中只包含索引键的话, MongoDB会直接从索引中来返回结果,这样就不用再扫描所有文档,也就不用将文档加入内存中,这样只包含索引字段的查询是非常的高效的。索引也可适用于管道操作符。例如下图所示:
二 索引类型
a. _id字段
默认情况下,所有MongoDB集合都有一个以_id字段命名的索引,一个12个字节的唯一标识符。如果用户没有给_id字段指定值的话,MongoDB会使用自动创建一个ObjectID对象,作为_id的值。_id索引是唯一的,用户不可以插入两条_id字段相同的文档。
注意: 1._id字段是不能删除的
2.在分片集群中,如果你不选择_id作为片键的话,你的程序必须确保能够产生一个唯一的_id值,否则会产生错误。在大多数情况下,都会使用ObjectID作为_id的值。
b.单个字段索引
可以在文档的任何一个字段上来建立索引,默认情况下_id字会作为索引
例如:下面是friends集合中的一个文档:
{
"_id" : ObjectID(...), "name" : "Alice" "age" : 27
}
将普通字段作为索引
db.friends.ensureIndex( { "name" :
1 } )
将子文档的字段作为索引
{
"_id": ObjectId(…)
"name": "John
Doe”
"address": {
"street": “Main"
"zipcode": 53511
"state": “WI”
}
}
db.people.ensureIndex( { "address.zipcode": 1 }
)
将子文档作为索引
{ _id: ObjectId("523cba3c73a8049bcdbf6007”),
metro: { city: "New York", state: "NY”},
name: "Giant Factory”
}
注意:1.在子文档的字段上创建索引和子文档索引是不一样的,例如:
下面的查询会用到metro索引
db.factories.find( { metro: {
city: "New York",
state: "NY" }
} )
使用上面的查询会返回整个文档。
2.在进行子文档查询的时候,字段顺序是比较重要的,例如,如果按照下面的方式来写查询语句是不会使用到先前创建的索引,因为它和我们文档中字段的顺序不一致。
db.factories.find( { metro: {
state: "NY",
city: "New York" }
} )
c.复合索引
MongoDB 允许在多个字段上建立索引。在创建复合索引中,字段出现的先后顺序会影响到查询。比如说如果有一个复合索引:{
userid: 1, score: -1 } ,集合内的文档首先按照userid进行排序,对于每一个相同userid会按照score排序。
{ "_id": ObjectId(...) "item": "Banana" "category": ["food", "produce", "grocery"] "location": "4th Street Store" "stock": 4 "type": cases "arrival": Date(...)
}
例如,我们可以在经常查询的item和stock字段上建立复合索引:
db.products.ensureIndex( { "item": 1, "stock": 1 } )
注意: 1.复合字段最多只能包含31个字段
2.复合索引的字段上是不能包含哈希索引字段的。
3.在建立复合索引的时候,字段的顺序是很重要的,例如在上面的例子中,文档首先会按照第一个字段的值来进行排序,对于每一个相同的值,再按照第二个字段给定的值进行排序。
对于复合索引,MongoDB支持前缀式的查询,例如:复合索引字段为 :{ a: 1, b: 1, c: 1
},当我们按照以下字段查询的时候,都会用到上面建立的索引: { a: 1 }和 {
a: 1, b: 1 }
对于 { "item": 1, "location": 1, "stock": 1 } MongoDB
支持下面的查询方法
{“item”:1}
{“item”:1,”location”:1}
{“item”:1,”location”:1,”stock”:1}
不支持下面的查询
{“stock”:1}
{“location”:1,”stock”:1}
索引顺序(升序降序索引)
db.events.find().sort( { username: 1, date: -1 } )
username将按照升序排序,而date将按照降序排序
d.多重(多路径)索引
MongoDB 使用多重索引来对数组元素排序。如果我们在一个拥有数组值的字段上建立索引的话,MongoDB会独立的为数组内的元素创建索引,多重索引可以在数组中查询某个特定的元素的时候或者数组的子集的时候可以加快查询。
{
"_id" : ObjectId("..."), "name" : "Warm Weather", "author" : "Steve", "tags" : [ "weather", "hot", "record", "april" ]
}
在数组tags1上建立索引 { tags: 1}
注意: 1.片键不能为多重索引
2.你不需要显式的的创建索引,Mongodb 会根据需要自动的创建多重索引
3.复合索引中可以包含多重路径索引,但是最多只能包含一个。比如我们可以在
下面的文档上:
{a: [1, 2], b: 1}
{a: 1, b: [1, 2]}
创建复合索引{ a: 1, b: 1 }是允许的。
但是如果在 {a: [1, 2], b: [1, 2]}这个文档上,是不能建立{a: 1, b: 1 }
e.地理位置坐标
地理位置坐标可以提高地理信息坐标信息的查询速度,MongoDB提供了两种索引类型:2d indexes和2sphere indexes。前者是基于平面几何来使用,后者使用的是WGS84地球坐标系,存储的数据需要是GeoJSON格式的对象。
MongoDB目前支持三种类型的GeoJSON 对象:点 线 多变形
点:{
loc : { type : "Point" , coordinates : [ 40, 5 ] }}
线:{ loc : {
type : "LineString" , coordinates : [
[ 40 , 5 ]
, [ 41 , 6 ]
]}}
多边形:{ loc : {
type : "Polygon” ,
coordinates : [ [ [ 0 , 0 ]
, [ 3 , 6 ]
, [ 6 , 1 ]
, [ 0 , 0 ]
] ] }}
注意:对于多边行来说,必须是闭合的,所有首尾两个点必须是相同的,这样的话一个多变形至少要有四个点。
球面索引支持按照GeoJSON对象和传统的经纬度对的数据,而后者是通过将传统的经纬度对转化为GeoJSON对象的Point类型来实现的,所以当我们存储地理位置信息的时候,最好按照GeoJSON对象的格式来进行存储。
MongoDB的二维球面索引支持所有的有关地理位置信息的查询:包含关系、相交关系、毗邻关系。
一个复合球面坐标索引可以包含一个文档的一个多个位置和非位置字段,可以将其按任意的顺序安排。
MongoDB 2.4版本使用的是WGS84坐标系来进行地理位置的相关计算,坐标轴的顺序为:<经度,纬度>。
注意: 1.只能在一个集合上创建一类位置索引:要么是2dsphere 或者是2d
2.不能将球面坐标索引作为片键 但是我们可以通过在一个分片集合上使用一个不同的字段作为片键,来使用球面坐标索引。
凹多边形的表示:
{ loc : { type : "Polygon" ,coordinates :
[
[ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ],
[[2,2],[3,3],[4,2],[2,2]]
]
}
}
注意:先写外层的多边形,后写内层多边形,内层多边形必须包含在外层多边形内,二者可以公用一条边。
平面索引
支持在欧几里德空间内计算位置关系,注意不能在包含GeoJSON对象的集合上使用2d索引。
点的存储:loc : [ <longitude> , <latitude> ]
f.哈希索引
支持基于哈希的分片集群,MongoDB提供了一个哈希索引的类型,将某一个字段的哈希值作为索引。使用哈希索引可以将数据随机的分配到集群中,如果使用哈希索引,需要注意的是哈希索引只支持匹配查询,不能进行范围查询。对于拥有子文档和数组的字段,不能使用哈希值来获取子文档。
可以使用下面的语句来建立一个哈希索引
db.active.ensureIndex( { a: "hashed" } )
g.文本索引
MongoDB提供了一个测试版的文本索引,用来快速查询一个集合中存储的字符串。
三 索引属性
a.唯一索引
对于索引项不能有相同的值出现
db.addresses.ensureIndex( { "user_id": 1 }, { unique: true } )
如果在复合索引字段上建立唯一索引的话,MongoDB会强调复合字段内的值必须是唯一的,而不是单个字段唯一。
如果插入一个文档,该文档的唯一索引字段没有值的话,MongoDb会给其赋null值。MongoDB只允许这样的文档出现一次,如果再插入一个空值文档的话,会产生错误(duplicate key error)。
b.稀疏索引(Sparse Index)
确保文档只包含有索引字段,即使给字段是一个null值也是可以的。Spare索引相当于一个过滤器,如果我们在某个字段上建立spare索引,当在稀疏索引字段上执行查询语句的时候,如果某条文档不存在Spare索引字段的话,将不会包含到结果中。
例如:某个集合有以下三条文档:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie” }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
在 spare字段上建立一个稀疏索引
db.scores.ensureIndex( { score: 1 } , { sparse: true } )
然后我们执行查询:
db.scores.find().sort( { score: -1 } ) 这样只有包含score字段的文档才会显示。
返回的结果将是:
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
C.唯一索引和稀疏索引的联合使用
例如:某个集合有以下三条文档:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie” }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
在score字段上添加一个唯一索引和一个稀疏索引
db.scores.ensureIndex( { score: 1 } , { sparse: true, unique: true } )
建立上面的索引后,我们可以插入文档字socre字段上的值要么是唯一的,要不不存在score字段。
例如:db.scores.insert( { "userid": "PWWfO8lFs1", "score": 43 } )
db.scores.insert( { "userid": "XlSOX66gEy", "score": 34 } )
db.scores.insert( { "userid": "nuZHu2tcRm" } )
db.scores.insert( { "userid": "HIGvEZfdc5” }
以上的文档都是允许插入的。下面的两条是不让插入的,因为集合中已经存在了score位82和90的两条文档。
db.scores.insert( { "userid": "PWWfO8lFs1", "score": 82 } )
db.scores.insert( { "userid": "XlSOX66gEy", "score": 90 } )