想来接触mongodb已经快一年了,对于它的索引知识也积攒了不少经验,趁着这个月黑风高的夜晚,就把mongodb的索引总结一番吧。
一,索引介绍
mongodb具有两类索引,分别为单键索引和复合索引。
1.单键索引是最简单的一种索引,创建单键索引的开销要比复合索引小很多。单键索引主要用于针对单值查询的条件。
2.复合索引是将文档中的几个键联合起来创建的一种索引,创建这种索引需要更多的空间与性能开销。分别体现在:
1).在给大量数据创建复合索引时,会阻塞数据库的查询,更不用说修改和插入操作了;
2).插入一条数据时,要花费更多的时间来给复合索引加数据;
3).创建的复合索引所站得空间大小根据数据的类型以及键的数量而有所不同。比如,如果你用五个NumberInt的键创建的复合索引的空间大小,并不会比两个NumberInt和一个String类型创建的复合索引占用更多的空间。索引在设计数据类型时,尽量将数据类型设置为NumberInt类型,以及尽量少使用string类型的数据做索引;
二,创建索引
创建索引的语句很简单。
1.单键索引的创建:db.test.ensureIndex({name:1},{name:'index_name'})
2.复合索引的创建:db.test.ensureIndex({name:1,age:1,sex:1},{name:'index_nas'})
三,索引优化
索引的优化是一个重头戏,需要详细的来解释。我得测试数据插入了100万条。字段分别为name,sex,type,time,id
1.我们来看一个简单的查询:db.test.find({name:'name_1'}) 相信大家对这个查询已经很熟悉了,然后我们来看看这个语句的索引执行计划:
{
"cursor" : "BasicCursor", 查询语句所用到的索引,而BasicCursor代表没有索引
"isMultiKey" : false, 是否为复合索引
"n" : 1, 查询到的结果数
"nscannedObjects" : 1000000, 扫描的文档数量
"nscanned" : 1000000, 扫面的索引数量
"nscannedObjectsAllPlans" : 1000000, //影响的所有的被扫描文档的总数量
"nscannedAllPlans" : 1000000, //所有被扫描的索引的总数量
"scanAndOrder" : false, 是否排序
"indexOnly" : false,
"nYields" : 2,
"nChunkSkips" : 0,
"millis" : 342, 花费的时间
"indexBounds" : {
},
"server" : "node1:27017"
}
从这个执行计划中可以看出,该条查询语句查询一条数据需要扫描整个表,这肯定扯淡了嘛,那这时候就该给这个字段创建索引了,创建一个单键索引
db.test.ensureIndex({name:1},{name:'index_name'})
创建完索引之后,再来查看看这条查询语句的执行计划:
{
"cursor" : "BtreeCursor index_name",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 1,
"nscannedAllPlans" : 1,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"name" : [
[
"name_1",
"name_1"
]
]
},
"server" : "node1:27017"
}
简直是逆天啊,nscanned和nscannedObjects居然从100万下降到1条,也就是查询数据时,只扫描了一条就已经找到,而且花费的时间是0秒,没有创建索引时,居然是342毫秒,绝对索引威武啊。
2.这时候我想通过type和sex来组合查询某一条件的数据: db.test.find({type:1,sex:0}) 看看这句的执行计划:
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 55555,
"nscannedObjects" : 1000000,
"nscanned" : 1000000,
"nscannedObjectsAllPlans" : 1000000,
"nscannedAllPlans" : 1000000,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 529,
"indexBounds" : {
},
"server" : "node1:27017"
}
从这个计划中可以看出,为了查找几万条数据,它也扫描了整个表,很显然,该创建索引了:
db.test.ensureIndex({type:1,sex:1},{name:'index_ts'})
创建完索引之后,再来执行查询语句,看看执行计划:
db.test.find({type:1,sex:0}).explain()
{
"cursor" : "BtreeCursor index_ts",
"isMultiKey" : false,
"n" : 55555,
"nscannedObjects" : 55555,
"nscanned" : 55555,
"nscannedObjectsAllPlans" : 55555,
"nscannedAllPlans" : 55555,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 112,
"indexBounds" : {
"type" : [
[
1,
1
]
],
"sex" : [
[
0,
0
]
]
},
"server" : "node1:27017"
}
很显然,绝对是一个最佳索引,因为n=nscannedObjects=nscanned了,而且查询时间从529毫秒下降到112毫秒了,这也是一个质的飞跃,可以明显的看到,它使用了刚刚创建的index_ts索引。
现在我又有一个需求了,我想通过时间再来排序,好的,我们执行查询语句: db.test.find({type:1,sex:0}).sort({time:-1}) 我们来看看这个查询语句的执行计划:
{
"cursor" : "BtreeCursor index_