mongoDB的索引

1 索引

  • 索引一般可以极大的提升查询的效率,若是没有索引,MongoDB在读取数据时必须扫描集合中的每一个文件并选取那些符合查询条件的记录。
  • 这种扫描全集合的查询效率是很是低的,特别在处理大量的数据时,查询能够要花费几十秒甚至几分钟,这对网站的性能是很是致命的。
  • 索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

2 创建索引

2.1 建立匿名索引

2.1.1 准备数据

  • users数据库中的classOne集合中添加 1000000 条文档
> use user
> for (var i = 1; i<= 1000000;i++) {
    users.push({name: 'Lily_' + i, num: i })
}
> db.classOne.insert(users);
/* BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 1000000, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] })*/
复制代码

2.1.2 查找{num: 1000000}文档的过程分析

> db.classOne.find({num: 1000000}).explain(true)
{
	"queryPlanner" : {
		"plannerVersion" : 1,
		"namespace" : "user.classOne",
		"indexFilterSet" : false,
		"parsedQuery" : {...},
		"winningPlan" : {
			"stage" : "COLLSCAN",   // 扫描全部的数据 
			"filter" : {...},
			"direction" : "forward"
		},
		"rejectedPlans" : [ ]
	},
	"executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 1,
		"executionTimeMillis" : 365,    // 本次查找执行时间的毫秒数
		"totalKeysExamined" : 0,
		"totalDocsExamined" : 1000000,
		"executionStages" : {
			...
		},
		"allPlansExecution" : [ ]
	},
	"serverInfo" : {
		...
	},
	"ok" : 1
}
复制代码

从这个过程的解析对象中,咱们得知查找{num: 1000000}文档,是经过COLLSCAN方式扫描的,执行的时间为 365msmongodb

2.1.3 建立匿名索引

在常常按照文档的倒序查找的应用场景中,咱们能够经过创建索引来进行查找,以节约咱们的查找时间。数据库

db.collection.ensureIndex(keys, options)
复制代码
  • 创建索引
> db.classOne.ensureIndex({num: 1})
/* { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, // 添加本次索引以前的索引数 "numIndexesAfter" : 2, // 添加本次索引以后的索引数 "ok" : 1 } */
复制代码
  • 创建索引后再次查找
> db.classOne.find({num: 1000000}).explain(true)
/* { "queryPlanner" : { ... "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", // 经过扫描索引进行查找 "keyPattern" : { "num" : 1 }, "indexName" : "num_1", // 若是没有指定索引名称的话, 默认格式为 ‘字段_1’ 或者 ‘字段_-1’ , 1 表明正序, -1 表明 倒序 "isMultiKey" : false, "multiKeyPaths" : { "num" : [ ] }, ... } }, ... }, "executionStats" : { "executionSuccess" : true, "nReturned" : 1, "executionTimeMillis" : 6, // 本次执行时间的毫秒数为 6ms !!! ... }, "serverInfo" : {...}, "ok" : 1 } */
复制代码

创建索引以前用时 365 ms , 创建索引以后查找用时须要 6 ms, 用时大大的减小了。数组

2.2 建立命名索引

若是没有指定索引名称的话, 默认格式为 fieldName_1 或者 fieldName_-1,若是想要自定义索引名称的话,能够在建立的时候指定名称,以下:缓存

> db.classOne.ensureIndex({name: 1}, {name:' myIndexName'})
/* { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } */
复制代码

2.3 查看索引

> db.classOne.getIndexes()
/*
[
	{
		"v" : 2,
		"key" : {
			"_id" : 1
		},
		"name" : "_id_",    // 原始的 根据 _id 字段 生成的索引 
		"ns" : "user.classOne"
	},
	{
		"v" : 2,
		"key" : {
			"num" : 1
		},
		"name" : "num_1",   // 根据 num 字段 升序 建立的索引 
		"ns" : "user.classOne"
	},
	{
		"v" : 2,
		"key" : {
			"name" : 1
		},
		"name" : " myIndexName",   // 本身命名建立的索引
		"ns" : "user.classOne"
	}
]
*/
复制代码

2.4 指定须要使用的索引

> db.classOne.find({num: "500000"}).hint({num:1}).explain(true)
复制代码

2.5 删除索引

db.collectionName.dropIndex(IndexName)  删除指定的索引(IndexName)

db.collecitonName.dropIndex('*');       删除全部索引
复制代码

2.6 在后台建立索引

db.collection.ensureIndex(keys, {background: true})
复制代码

2.7 创建多键索引

mongodb能够自动对数组进行索引bash

> db.classOne.insert({hobby:['basketball','football','pingpang']});
> db.classOne.ensureIndex({hobby:1});
> db.classOne.find({hobby:'football'},{hobby:1,_id:0}).explain(true);
复制代码

2.8 复合索引

查询的条件不止一个,须要用复合索引session

db.collection.ensureIndex({name:1,num:1});
复制代码

2.9 过时索引

在必定的时间后会过时,过时后相应数据数据被删除,好比session、日志、缓存和临时文件数据结构

> db.classTwo.insert({time:new Date()});
> db.classTwo.ensureIndex({time:1},{expireAfterSeconds:10});
复制代码

note性能

  • 索引字段的值必须Date对象,不能是其它类型好比时间戳
  • 删除时间不精确,每60秒跑一次。删除也要时间,因此有偏差。

2.10 全文索引

大篇幅的文章中搜索关键词,MongoDB为咱们提供了全文索引网站

2.10.1 建立全文索引

db.colleciton.ensureIndex({field: 'text'})
复制代码

note: text- 建立全文索引,1 - 升序索引,2 - 降序索引spa

2.10.2 usage

语法

  • $text: 表示要在全文索引中查东西
  • $search: 后边跟查找的内容, 默认所有匹配

举个栗子

  • 1 准备数据
// 原始数据
{ "_id" : ObjectId("5afa93eae82637e49ce12077"), "name" : "Lily", "content" : "I am a girl" }
{ "_id" : ObjectId("5afa93f6e82637e49ce12078"), "name" : "Tom", "content" : "I am a boy" }
{ "_id" : ObjectId("5afa9561e82637e49ce12079"), "name" : "Carl", "content" : "I do not know boy girl" }
复制代码
  • 2 建立全文索引 根据content字段建立一个全文索引
db.classThree.ensureIndex({content: 'text'})
复制代码
  • 3 根据索引查询数据
// 查找包含 ‘girl’ 的文档
> db.classThree.find({$text: {$search: 'girl'}})
// { "_id" : ObjectId("5afa93eae82637e49ce12077"), "name" : "Lily", "content" : "I am a girl" }


// 查找包含 ‘boy’ 的文档
> db.classThree.find({$text: {$search: 'boy'}})
// { "_id" : ObjectId("5afa93f6e82637e49ce12078"), "name" : "Tom", "content" : "I am a boy" }


// 查找包含 ‘girl’ 或者 ‘boy’ 的文档(屡次查找是 与 的关系)
> db.classThree.find({$text: {$search: 'boy girl'}})
/* { "_id" : ObjectId("5afa93eae82637e49ce12077"), "name" : "Lily", "content" : "I am a girl" } { "_id" : ObjectId("5afa9561e82637e49ce12079"), "name" : "Carl", "content" : "I do not know boy girl" } { "_id" : ObjectId("5afa93f6e82637e49ce12078"), "name" : "Tom", "content" : "I am a boy" } */


// 查找仅包含‘girl’ 不包含‘boy’的文档
> db.classThree.find({$text: {$search: 'girl -boy'}})
// { "_id" : ObjectId("5afa93eae82637e49ce12077"), "name" : "Lily", "content" : "I am a girl" }


// 就是要查找包含 ‘girl boy’ 字符的文档(须要转义)
> db.classThree.find({$text: {$search: '\"boy girl\"'}})
// { "_id" : ObjectId("5afa9561e82637e49ce12079"), "name" : "Carl", "content" : "I do not know boy girl" }
复制代码
  • 4 note

屡次查找,多个关键字为或的关系,中间以空格隔开

支持转义符的,用\斜杠来转义

3 二维索引

mongodb提供强大的空间索引能够查询出必定落地的地理坐标

3.1 建立 2d 索引

db.collection.ensureIndex({field:'2d'}, options)
复制代码

3.2 举个栗子

  • 1 准备数据
{ "_id" : ObjectId("5afaa078e82637e49ce1207a"), "gis" : [ 1, 1 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce1207b"), "gis" : [ 1, 2 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce1207c"), "gis" : [ 1, 3 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce1207d"), "gis" : [ 2, 1 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce1207e"), "gis" : [ 2, 2 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce1207f"), "gis" : [ 2, 3 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce12080"), "gis" : [ 3, 1 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce12081"), "gis" : [ 3, 2 ] }
{ "_id" : ObjectId("5afaa078e82637e49ce12082"), "gis" : [ 3, 3 ] }
复制代码

以下图:

2d索引

  • 2 建立 2d 索引
> db.map.ensureIndex({gis:'2d'})
/* { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } */
复制代码
  • 3 查询距离[1,1]最近的四个点
> db.map.find({gis:{$near: [1,1]}}).limit(4)
/* { "_id" : ObjectId("5afaa078e82637e49ce1207a"), "gis" : [ 1, 1 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207b"), "gis" : [ 1, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207d"), "gis" : [ 2, 1 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207e"), "gis" : [ 2, 2 ] } */
复制代码

查询结果以下图:

near

  • 4 查询以点[1,2]和点[3,3]为对角线的正方形中的全部的点
> db.map.find({gis: {$within:{$box: [[1,2],[3,3]]}}})
/* { "_id" : ObjectId("5afaa078e82637e49ce1207b"), "gis" : [ 1, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207e"), "gis" : [ 2, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207c"), "gis" : [ 1, 3 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207f"), "gis" : [ 2, 3 ] } { "_id" : ObjectId("5afaa078e82637e49ce12081"), "gis" : [ 3, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce12082"), "gis" : [ 3, 3 ] } */
复制代码

查询结果以下图:

box

  • 5 查出以[2,2]为圆心,以1 为半径为规则下圆心面积中的点
> db.map.find({gis: {$within:{$center: [[2,2],1]}}})
/* { "_id" : ObjectId("5afaa078e82637e49ce1207b"), "gis" : [ 1, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207d"), "gis" : [ 2, 1 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207e"), "gis" : [ 2, 2 ] } { "_id" : ObjectId("5afaa078e82637e49ce1207f"), "gis" : [ 2, 3 ] } { "_id" : ObjectId("5afaa078e82637e49ce12081"), "gis" : [ 3, 2 ] } */
复制代码

查询结果以下图:

center

note: [1,1]等角落里的坐标与[2,2]的坐标距离是√2

4 索引使用的注意事项

  • 1为正序 -1为倒序
  • 索引虽然能够提高查询性能,但会下降插件性能,对于插入多查询少不要创索引
  • 数据量不大时不须要使用索引。性能的提高并不明显,反而大大增长了内存和硬盘的消耗。
  • 查询数据超过表数据量30%时,不要使用索引字段查询
  • 排序工做的时候能够创建索引以提升排序速度
  • 数字索引,要比字符串索引快的多
相关文章
相关标签/搜索