说明:本篇文章介绍常用的索引创建的方式与删除,另外介绍 过期索引的使用,以及查询分析(explain 函数的使用)
MongoDB 索引
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
MongoDB 的索引算法主要是使用 btree 与 hash 算法。默认是使用 btree。
MongoDB 两种创建索引的方式
自动创建
当我们在 MongoDB 的集合中插入数据之后,会自动创建一个 "_id" 的 key值,这个 key 上面就会有 MongoDB 自动创建的索引在。
手动创建
我们使用 createIndex() 语法创建索引。在 3.0.0 版本之前,是使用 ensureIndex() 创建索引。
语法
db.collection.createIndex(keys, options)
参数说明:
- key:创建索引的字段, 1 为升序创建索引,-1 为降序创建索引。
- options:传入不同的参数,用于生成不同的索引(比如唯一索引,哈希索引),以及在后台运行
索引的分类
普通索引 ({field:1/-1})
唯一索引 ({filed.subfield:1/-1}, {unique:true})
稀疏索引 ({field:1/-1},{sparse:true})
哈希索引 ({file:'hashed'})
说明
稀疏索引的特点:如果针对field做索引,针对不含field列的文档,将不建立索引。与之相对,普通索引,会把该文档的field列的值认为NULL,并建索引。应用场景:小部分文档含有某列时。
哈希索引速度比普通索引快,但是,无法对范围查询进行优化。适宜于:随机性强的散列。
使用
// 以 title 字段创建升序索引
db.collection.createIndex({"title":1})
// 使用多个字段创建索引
db.collection.createIndex({"title":1, "name": 1})
// 创建唯一索引
db.collection.createIndex({"name" : 1}, {unique:true})
// 后台创建索引
db.collection.createIndex({"title" : 1, "name": 1}, {background: true})
// 查看集合索引
db.collection.getIndexes()
// 查看集合索引大小
db.collection.totalIndexSize()
// 创建子文档索引
db.collection.createIndex({filed.subfield:1/-1});
// 例如
db.collection.createIndex({"product.price":1});
// 创建哈希索引
db.collection.createIndex({"name" : 'hash'});
部分操作截图:
过期索引
过期索引:是指在一段时间过后会过期的索引(感觉像是废话)。重点是在索引过期之后,相应的数据会被删掉。这样式适合存储一些在一段时间需要丢弃掉的数据。
需要说明的是:这个过期索引的时间并不是特别准时。比如设置的是 10s,但是 10s 之后并不一定会删除。可能需要等待的时间更加长一些。
建立过期索引
// field 必须是日期格式字段
db.collection.createIndex({filed : 1/-1}, {expireAfterSeconds: 秒为单位的数值 });
示例:给集合中的 name 字段加上 过期索引,30秒之后过期。
db.collection.createIndex({"date": 1}, {"expireAfterSeconds" : 30})
过期索引特别说明
- 存储在过期索引字段的值必须是指定的时间类型(ISODate 或者 ISODate 数组)
- 如果指定了ISODate()数组,则按照最小的时间进行删除。
- 过期索引不能是复合索引,因为我们不能指定两个过期时间索引。
- 删除过程是由后台程序每60s跑一次,而且删除也需要一些时间,索引存在误差
删除索引
示例:删除所有索引(只能删除非 "_id" 的索引)
db.collection.dropIndexes()
示例:删除集合指定的索引
db.collection.dropIndex({filed : 1/-1})
// 例如
db.collection.dropIndex({"title" : 1 });
使用 explain() 分析
使用 explain() 函数分析查询。
# 语法
db.collection.find().explain();
没有创建索引的扫描
图示中使用的是全集合扫描(没有创建索引):
创建索引并且扫描
创建索引之后分析如下:
// 添加索引
db.products.createIndex({"sku":1});
// 输出结果
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
// 执行查询分析
db.products.find({"sku":"abc123"}).explain();
// 输出结果
{"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "study.products",
"indexFilterSet" : false,
"parsedQuery" : {
"sku" : {
"$eq" : "abc123"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"sku" : 1
},
"indexName" : "sku_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"sku" : [
"[\"abc123\", \"abc123\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "GZ-YY-WQ",
"port" : 27017,
"version" : "3.2.6",
"gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25"
},
"ok" : 1
}
注
当我们使用两个字段作为查询条件时,其中一个字段没有索引,另一个有索引,使用 explain() 函数执行结果显示也是 全集合查询。