在第一篇的文章末尾我们提到了索引,下面就将从不同的索引类型,索引的机制展开来介绍MongoDb的索引应用。
- 为什么需要索引
- 单字段索引
- 复合索引
- 多Key索引
- 文本索引
- Hash索引
- 索引的额外属性
- 当你抱怨MongoDb的查询效率低下的时候,可能你就需要考虑索引了,先科普MongoDb里面的索引机制,当你往MongoDb插入数据的时候,每个文档经过底层的存储引擎持久化数据,会生成一个位置信息,通过这个位置信息就能通过存储引擎读出该文档,在MMAP中 位置信息是「文件Id + 文件的Offset」;在WT存储引擎中(KV存储引擎),位置信息是WT在存储文档的时候生成一个Key,通过这个Key就能访问到文档,位置信息我们用Position来表示。
![85d84916fbfdc9f82da7cb146024d1f8.png](https://i-blog.csdnimg.cn/blog_migrate/7c81b4a25998ce5804a182a763f443fb.jpeg)
插入Document之后,生成的位置信息如下图:
![32dd37607bfdf5e76576d6bb2a5abec3.png](https://i-blog.csdnimg.cn/blog_migrate/e706b1236ad0dd6b9fe5ac6232124d01.jpeg)
假如现在查询年龄为10的所有文档,这时候需要遍历所有的Document【全表扫描】,根据位置信息读出年龄为10的文档,当然如果表里面的数据很少的话不会有什么性能问题,但是如果数据达到几百万,甚至过亿的时候,对集合的全表扫描开销是非常大的,一个查询可能耗费数十秒甚至几分钟都有可能。
如果想加速上面的查询,我们就需要对Age字段建立索引 ,db.persons.createIndex({age:1}) //对Age字段建立升序索引,建立索引之后,mongodb额外会存储一个升序排序的索引数据,索引通常采用B-Tree的结构持久化,以保证快速的从索引里面找到对应的Age位置信息(position),然后找出对应的Document。
![de4ecbd06794b0ed5df706ab26e95244.png](https://i-blog.csdnimg.cn/blog_migrate/480ef4241324c6d624f0ad122d3c44f7.jpeg)
通常来说,索引就是将文档按照某个字段顺序组织起来,以便能根据这个字段进行高效查询,有了索引能优化以下的场景:
- 查询年龄为10的所有文档
- 更新,删除年龄为10 的文档,查询和更新需要根据条件来找出所有符合的数据进行更新,本质上也是查询优化。
- 排序,按照年龄进行排序,如果没有索引,也会进行全表扫描,然后对扫描的结果进行排序
- 同时MongoDb会自动为默认的_id 字段增加索引
单字段索引
db.person.createindex({age:1}) 上述语句对age字段创建来索引,使其能加速对Age字段各种查询,这是一种最常见对索引,mongodb创建对默认索引_id,也是这样的,{age:1}代表升序索引 ,{age:-1}代表降序索引,对于单字段索引来说,升序和降序都是一样的效果
复合索引
复合索引是单字段索引的升级版本,它对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按照第二个字段排序,依次类推,如下我们创建一个Age,name复合索引
db.createIndex({age:1,name,:1});
上述索引对应的的数据组织如下图,先按照Age字段排序,然后在按照Name字段排序,
![11aafa3e8c5d1b26d16d4ccbb784705f.png](https://i-blog.csdnimg.cn/blog_migrate/cacda2738e1c58ef79e2fb0fa21a22bd.jpeg)
复合索引能满足比单字段索引更丰富单应用场景,不光能满足单个字段组合起来查询,同时还能满足所有前缀字段的查询,这里的 {age:1} 就是{age:1,name:1}索引的前缀,所以类似的db.persons.find({age:1}) 也会使用这个复合索引,但是 {name:1} 就不会使用这个索引,如果要经常根据Name字段来查询,应该创建{name:1,age:1}这样的索引查询name的时候进行了全表的扫描
![5aa34fbea9d7734f6915ea6e14b2c288.png](https://i-blog.csdnimg.cn/blog_migrate/787aa00e585337cf920735c776af225b.jpeg)
对Age进行查询的时候则是使用了复合索引来查询的
![3e4b974b647225ec11fba9689f576060.png](https://i-blog.csdnimg.cn/blog_migrate/844e836bb0d5d05b936366136fc4560b.jpeg)
多Key索引
当索引的字段为数组时,创建出来的索引为多Key索引,多Key索引会为数组的每个元素都创建一条索引,比如我们在persons表在增加一个habbit字段「数组」,需要查询由相同爱好的人,就可以利用多Key索引
![53892868a24a3b9072c59e7ee00707e2.png](https://i-blog.csdnimg.cn/blog_migrate/cdd0db68fbc141a4dab717f80fdcb68d.jpeg)
Hash索引
哈希索引是按照某个字段的Hash值来建立索引的,目前主要应用于MongoDb Shard Cluster的Hash分片,并且Hash索引只能满足字段的完全匹配,不能满足范围查询。
地理位置索引(2d索引)
能很好解决020的应用场景,查找某一位置的附近区域
文本索引
主要用来解决快速文本的查找,例如博客的内容,如果按照博客的内容查找,就要建立文本索引,并且支持分词。
下面在介绍一下索引的一些特性
- 唯一索引:(unique index)保证索引的字段不会出现相同的值,例如_id就是唯一索引。
- TTL索引:可以针对某个时间字段,指定文档的过期时间。
- 部分索引:(partial index)只针对复合某个特定条件的文档来建立索引,并且3.2以后的版本才支持。
- 稀疏索引:(sparse index)只针对存在索引字段的文档建立索引,可以看作是部分索引的一种特殊的情况。
最后我们说一下,db profiling ,mongoDb支持对请求进行Profiling ,目前仅支持三种级别
- 0, 不开启Profilling
- 1,将处理时间都超过某个阀值的请求记录到system.profile集合
- 2,将所有的请求都记录到system.profile集合
在生产环境中我们建议使用1,来监视慢的请求,来做进一步的优化。