mongodb系列02-------深入理解索引原理

  1. 云栖社区>
  2. 博客>
  3. 正文

mongodb系列02-------深入理解索引原理

随风_1993 2017-12-08 14:17:23 浏览3512
展开阅读全文

1
之前对index的了解仅仅是停留在使用上面,一直都不太清楚index的原理没,那段时间专门看了一下,这里我梳理一下
2
如果我们想要理解索引的原理,我们首先要了解一种最基本的数据结构Btree 就是balance tree ,这个图就是多路平衡排序树的一个例子,我们了解到每个节点的存储是按key-valuede形式存放的,一般一个节点的大小不会哦超过一个磁盘块的大小,这很重要,因为这关系到一个非常重要的指标IO次数,
2,多路的,它不是二叉树,
3,平衡树,数据有序,右侧数据一定会大于等于左侧的数据,并且平衡,有利于数据的快速检索,
4,B树我们知道它一般非叶子节点都用来存放索引信息,而真正的数据都存放于叶子结点
3
我们都知道当我们为表插入数据时需要指定对应的Primary Key ,我们都知道的是Id为了唯一区别当前这条数据,很多人不知道的是Id还是构成数据库表主索引的关键字,就是说阿,我一旦创建了表指定PK之后,系统会自动创建一个以ID为关键字的构成的以Btree为数据结构的主索引表,那为什么要这样做呢,原因很简单,因为最常见的查询都是根据Id进行的,你可以做在数据量比较大的数据库表上面做一个实验,分别根据PK查询和非索引字段查询,比较一下时间就看得出来,一般Id的查询时非常快的。那么通常意义上所说的索引是什么样的呢。,一旦我们为数据库表的某一个字段创建index以后,操作系统会为数据库表生成一个Btree并和数据库数据一起存储在Disk上,而且这个Btree的节点的value就是我们选定的那个数据库表的字段。
4
上图解释了一个简单的查询的过程,当程序发起这样一个查询时,首先主索引表会被load到内存中去,解析sql语句找到关键字value是1256,然后对主索引表开始进行查询,找到value为1256的节点,然后怎么办呢,我们还没有找对1256对应的这条数据在硬盘上的真正位置,没关系Btree已经为我们想好了,前面说过Btree的真正数据都存放在叶子节点,这里1256对应的节点的子节点就是叶子节点,叶子节点中存放的就是1256这条数据存放在硬盘上的地址,系统找到这个地址后,如果这条数据只是占用了一个磁盘块大小的空间,那么系统只需要再访问一次磁盘就可以了,所以之所以Id查询的速度快的原因就是IO次数很少,其时间复杂度为logn
5
还有一点我们必须知道的是非主索引(非聚集索引)与主索引的关系的,比如说我们在User表上面的name字段建立了一个索引,那现在User 表有两个索引表了,如果我们发起了
Select * from user where name=’tom’
这个sql的执行过程我们现在可以看得很清楚,首先name索引表load内存,找到value=‘tom’
的节点,然后进入叶子节点,这里注意非主索引表的叶子节点存放的是这行数据的ID值,这么设计当然是非常合乎情理的,根据叶子节点确定该行数据Id值,然后load主索引表到内存中,根据Id再查找这行数据真正的地址
6
现在你明白为什么name上没有索引的时候,查询超级慢呢,就是因为这种查询方法是全表扫描,就是从头到尾一行一行的把数据load到内存中查看name是否匹配,最坏的情况就是把整个表的数据查询一边,IO 次数太多了,严重影响了查询的时间。
现在可以确定Btree的使用确实大大提高了查询速度,我们需要来思考一下为什么偏偏选用了Btree这种我们不是那么熟悉的数据结构来做索引?

1.第一个问题为什么没有选用Hash structure?
这个疑问应该是很多人都回想到的,因为如果用Hash表来做索引表是什么样的呢,优点是查询速度更快了,试想一下是不是,我们可以把每一行数据的地址都记录在索引表里面阿,查询的时候只要把hash索引表load内存,在冲突比较少的情况下查一次就可以确定该数据的位置了,时间复杂度是O(1),确实非常快的,熟悉计算机组成原理的同学应该非常清楚 hash索引在cache,虚存以及文件系统的设计中有大量的应用,其最大优势就是查询速度快,明显的空间换时间。 那它的缺点当然也是非常突出的,很简单如果你的数据库表数据量非常大那么想当然的索引表也非非常之大,这对于内存的利用率来说还是不好的,Hash 表无法当作数据库索引还有一个致命缺点就是,它无法进行范围的查询,hash函数的映射只是支持单值查询,如果selct * from use where age>12 这样的查询Hash表是无法实现的,当然mongodb支持创建Hash索引,后面我还会讲到。
2.为什么没有选用BST(平衡二叉树)?
BST 和Btree有很多类似,我们应该更熟悉BST,有序并且平衡,,但是当数据量比较大时,Btree将更加的扁平,高度更低,查询速度更快
7
当我们为mongodb插入文档时,如果不指定_id,mongodb会自动生成id字段作为当前文档的唯一标识,和mysql是一样的,也就是说每一个文档都会有自己唯一的id mongodb会利用id来建立Btree主索引,这是默认建立的
8
单字段索引,比如我们在person文档的age字段上建立索引,语句如下,,和之前所讲的一样,利用age字段为索引结点建立age索引表,叶结点存储主索引的值,查询过程是,通过age找到对应id,再通过id主索引找对对应的文档,,这里的参数可以指定索引的排序,1 为升序 -1为降序,
然后我们看复合索引,既是在两个或是两个以上的字段建立联合索引,原理是一样的,联合age和name两个字段组织索引结点生成索引表,叶结点还是指向主索引表,也就是说,不管ji不管jianl不管建立多少索引,查询时最终还是会指向主索引表去查询文档,当然也有例外,比如覆盖索引我们后面会提到

   索引性能的分析

索引的建立对于数据库性能有什么影响? 建立索引有没有什么原则?
我们了解了索引的原理之后知道,查询的时候,如果查询条件不依赖PK,如果不建立索引就会引发全表扫描,如果数据量比较大时,全表扫描根本不可行,建立索引可以直接加快检索速度,IO 也呈指数即下降,那sh那是不是索引建的越多就愈好尼,也不尽然,因为查询期间索引表需要load到内存中,索引也不是越多越好,数据量比较大时,索引也会占去相当一本分内存,,而且索引应该建立在一些不经常更新并且数据重复量比较少的字段上,如果这个字经常更新,其对应的索引也需要更新,这个消耗比较高,还有就是如果这个字段的数据在数据库的重复率较高的话,建立索引的意义就没有那么大了,比如查找age=20的结果非常多,这样的效率跟全表扫描不相上下,,还有就是有时候复合索引建立,如果我们要在age和name上建立索引,只需建立两个字段的复合索引,复合索引表中结点是有两个字段组成,不用单个在两个字段上都建立索引,便可以实现高效查询。

OK多说无益为了证明我的观点正确我还是要做一点实验的,我会借助mongodb自带的explain 函数来证明我前面的关键
9
这个查询是没有在age字段建立索引之前发起的查询,explain函数可以监控查询过程,是一个非常好用的函数,可以看到 “COLLSCAN” 立即明白这个查询过程是全表扫面,,collection scan
Collection就是mongodb的一个表的单位,ok 然后我建立索引以后,再查一次看看有什么变化
10
能看出 扫描端倪吗,我们看内层的stage:INXSCAN index scan 使用了索引扫描,,,外层stage:Fetch 大家应该可以猜得到了,这里应该是去查主索引了没错了 。 ok 我觉得索引应该已经讲得非常明白了,如果有什么不理解可以再去baidu看看。

网友评论

登录后评论
0/500
评论
随风_1993
+ 关注