一、索引介绍
索引就是用来加速查询的,数据库索引与书籍的索引类似:有了索引就不需要翻遍整本书,数据库则可以直接在索引中查找,使得查找速度能提高几个数量级。在索引中找到条目以后,就可以直接跳转到目标文档的位置。
索引是一种特殊的数据结构,该数据结构将收集的数据集的小部分存储在一个易于遍历的表单中。索引存储特定字段或字段的值,该值由该字段的值排序。索引项的排序支持高效的平等匹配和基于范围的查询操作。此外,MongoDB可以通过使用索引中的排序返回已排序的结果。
在MongoDB索引支持提高查询执行效率,没有索引,MongoDB必须执行采集扫描,即扫描每一个文件的集合。
索引的优点:
1. 大大减少了服务器需要扫描的数据量。
2. 索引可以帮助服务器避免排序或使用临时表。
3. 索引可以将随机I/O转换为顺序I/O。
下图说明了使用索引选择匹配文档并对其进行排序的查询:
另外说一下MongoDB的索引使用BTree索引,对于什么是BTree索引可以去看本博客MySQL索引介绍部分。
二、默认 _id 索引
MongoDB 在创建集合期间在 _id 字段上创建一个唯一索引。
_id 索引阻止客户端为 _id 字段插入具有相同值的两个文档。不能在 _id 字段上删除此索引。
注意:在分片群集中,如果不使用 _id 字段作为分片键,则应用程序必须确保 _id 字段中值的唯一性,以防止出错。
这通常是通过使用标准的自动生成的 objectid 来完成的。
三、索引类型
1. 单键索引
除了 MongoDB 定义的 _id 索引外,MongoDB 还支持在文档的单个字段上创建用户定义的上升降序索引来支持重要的查询和操作。
对于单字段索引和排序操作,索引键的排序顺序 (即升序或降序) 并不重要,因为 MongoDB 可以在任一方向遍历索引。
1 |
2. 复合索引
MongoDB还支持多个字段上的用户定义索引,即复合索引。其中每个索引项可以包含有集合中文档的多个键,MongoDB允许复合索引最多31个键。
复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由 {userid:1, score:-1} 组成,索引首先按 userid 排序,然后在每个 userid 值中按 score 排序。
对于复合索引和排序操作,索引键的排序顺序(即升序或降序)可以确定索引是否可以支持排序操作。
3. 多键索引
MongoDB使用多键索引为存储在数组中的内容编制索引。
如果对包含数组值的字段进行索引,MongoDB将为数组的每个元素创建单独的索引项。这些多键索引允许查询通过在数组的一个或多个元素上匹配来选择包含数组的文档。
当收到查询时,这些多键索引让MongoDB可以利用数组的值返回相应文档作为结果。
如果索引字段包含数组值,MongoDB将自动确定是否创建多键索引;你不需要显示地指定索引为多键类型。
4. 空间索引
Mongodb支持地理空间坐标数据的高效查询,随着移动互联网的兴起,地理空间索引也变得越来越流行,如找到离当前位置最近的N个场所。MongoDB提供了一系列的索引和查询机制来处理地理空间信息。
在您存储地理数据和编写查询条件前,首先,您必须选择表面类型,这将被用在计算中。您所选择的类型将会影响您的数据如何被存储,建立的索引的类型,以及您的查询的语法形式。
MongoDB提供了两种表面类型:
球面
如果需要计算地理数据就像在一个类似于地球的球形表面上,您可以选择球形表面来存储数据,这样就可以使用2dsphere索引。您可以按照坐标轴:经度,纬度的方式把位置数据存储为GeoJSON对象。GeoJSON的坐标参考系使用的是WGS84数据。
平面
如果需要计算距离,就像在一个欧几里德平面上,您可以按照正常坐标对的形式存储位置数据并使用2d索引。
地理空间索引同样可以由createIndex来创建,只不过参数不是1或者-1,而是“2d”:
1 |
> db.colltest.createIndex({name:'2d'}) |
5. 文本索引
MongoDB提供的文本索引类型支持搜索集合中的字符串内容。这些文本索引不存储特定的语言停止的话(例如,“”,“一个”,“或”)和干的话,在一个集合中,只存储根词。
6. 哈希索引
MongoDB中哈希索引会对键的值计算一个哈希值然后索引该哈希值。这个索引支持相等匹配,可能会适合作为某些集合的分片键。哈希索引项中存储的是被索引键的哈希值。哈希函数会折叠子文档并计算整体值的哈希值,但是不支持多键(比如数组)索引。MongoDB可以使用哈希索引来支持相等查询,但是哈希索引不支持范围查询。
您可能无法创建一个带有哈希索引键的复合索引或者对哈希索引施加唯一性的限制。但是,您可以在同一个键上同时创建一个哈希索引和一个递增/递减(例如,非哈希)的索引,这样MongoDB对于范围查询就会自动使用非哈希的索引。
1 |
> db.colltest.createIndex({'name':'hashed'}) |
四、索引属性
1. 唯一索引
唯一索引可以让MongoDB拒绝保存那些被索引键的值已经重复的文档,如果希望创建一个唯一索引,请使用db.collection.createIndex()方法并将unique选项设置为true。
1 |
> db.colltest.createIndex({"name":1},{unique:true}) |
创建唯一索引时,如果集合中以及有相同的键,那么创建此索引就会报错。
2. 稀疏索引
稀疏索引中值存储那些有被索引键的文档的索引项,即使被索引键的值是null也会被索引(译者注:请注意,这里对null的处理和那些特殊索引的默认稀疏特性有细微差别,比如文本索引,2d索引等)。索引会跳过所有不包含被索引键的文档。这个索引之所以称为 “稀疏” 是因为它并不包括集合中的所有文档。与之相反,非稀疏的索引会索引每一篇文档,如果一篇文档不含被索引键则为它存储一个null值。
1 |
> db.colltest.createIndex({"name":1},{sparse:true}) |
3. 部分索引
部分索引仅索引满足指定筛选表达式的集合中的文档。通过为集合中的文档子集编制索引,部分索引具有较低的存储要求,并降低了索引创建和维护的性能成本。
部分索引提供了稀疏索引功能的超集,应该优先于稀疏索引。
4. TTL索引
TTL索引是MongoDB可用于在一段时间后自动从集合中删除文档的特殊索引。
这对于某些类型的信息 (如计算机生成的事件数据、日志和会话信息) 非常理想,这些信息只需要在数据库中保留有限的时间。
请参阅: 通过设置 ttl 来过期集合中的数据, 以获取实现说明。
五、索引管理
创建索引
下面创建一个生序的单列索引,默认是在前台运行,会对所有操作产生阻塞,生产环境风险很大,不建议使用。
1 |
> db.colltest.createIndex({"name":1}) |
在MongoDB上建索引可能会对MongoDB集群对可用性产生负面影响。在生产服务上,如果针对一个大集合触发建立索引,且在前台运行,你可能会发现,在索引建完之前,整个集群都无影响。在一个大集合上,这个过程可能会持续几个小时。
解决的方法很简单,MongoDB 提供了两种建索引的访问,一种是 background 方式,不需要长时间占用写锁,但索引建立比较慢;另一种是非 background 方式,需要长时间占用锁,但索引建立较快。使用 background 方式就可以解决问题,只需指定“background:true”即可。
createIndex 方法一共有两个参数,第一个是keys,第二个是options。keys就是用来指定要创建索引的列和排序方式,options用来指定索引的属性。
1 |
> db.colltest.createIndex({"name":1},{background:true}) |
在 options 中还可以指定索引名称,比如:
1 |
> db.colltest.createIndex({"name":1},{name: 'idx_name', background: true}) |
需要注意,千万不能写成 {“name”:1},{name: ‘idx_name’},{background: true} 形式,这样就不是在后台创建索引了,第三个参数会被忽略掉。
查看索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
> db.colltest.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "colltest.colltest" }, { "v" : 1, "key" : { "name" : 1 }, "name" : "name_1", "ns" : "colltest.colltest" }, |
上例显示出来的一共有2个索引,其中_id
是创建表的时候自动创建索引,此索引是不能够删除的。
或者
1 |
db.getCollection('collection').getIndexes(); |
如果想查看库中所有索引,需要先列出所有集合。然后遍历每一个集合即可。
1 2 3 4 5 |
var ops = db.getCollectionNames() for(i = 0; i < ops.length; i++){ var s = db.getCollection(ops[i]).getIndexes(); print(s[0].ns) } |
删除索引
若要删除索引,请使用 db.collection.dropIndex() 方法指定索引名称,并且指定生序还是降序。
1 |
> db.colltest.dropIndex({name:1}) |
上述命令是用来删除集合中某个索引,如果要想删除集合上的所有索引,就是用如下语法即可。
1 |
> db.colltest.dropIndexes() |
修改索引
若要修改现有索引,需要删除并重新创建索引。
此规则的例外是 TTL 索引,可以通过 collMod 命令与索引集合标志一起修改。
六、使用explain
MongoDB提供了一个Explain命令让我们获知系统如何处理查询请求。利用explain命令,我们可以很好地观察系统如何使用索引来加快检索,同时可以针对性能优化索引。使用explain命令返回查询使用的索引情况,耗时,扫描文档数等等统计信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
> db.colltest.find({name:'jerry'}).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "colltest.colltest", "indexFilterSet" : false, "parsedQuery" : { "name" : { "$eq" : "jerry" } }, "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "name" : 1 }, "indexName" : "name_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "name" : [ "[\"jerry\", \"jerry\"]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "localhost.localdomain", "port" : 27017, "version" : "3.2.0", "gitVersion" : "45d947729a0315accb6d4f15a6b06be6d9c19fe7" }, "ok" : 1 } |
<延伸>