MYSQL的索引
1、概念
索引:它提高数据库的性能。不用加内存,不用改程序,只要执行正确的create index ,查询速度就可能提高成百上千倍。但是所有的东西都是平衡的,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值在于,提高一个海量数据的检索速度。
形成:当我们建立一个表的时候,无论如何肯定会有索引。如果我们建立的时候设置了主键、唯一键、普通索引等,MYSQL会生成两个文件,一个是专门存数据的文件,另一个是索引文件(里面存储了索引结构);如果一开始我们没有设定主键、唯一键、普通索引等,MYSQL内核会自动生成一个索引列,也就是还会默认生成一个索引文件,只不过对我们是隐藏的。
2、底层结构
其实很早的时候,在学校学习的数据库的时候,老师在介绍索引的时候,会把索引比喻成书的目录,找一本书里的内容通过目录即可快速定位到我们要查找的内容,如果没有目录,我们寻找内容需要一页一页的寻找。这个比喻有一部分对,不过当时老师也是为了便于我们理解而这样比喻,其实数据库的索引比目录要复杂一些。
在早期的MYSQL,也就是MYISAM引擎是使用的B树去实现索引的,后来的INNODB引擎使用的是B+树去实现的。接下来分别介绍这两个结构。
2.1、B树结构
2.1.1、概念
结构:B数是一个类似二叉搜索树的树,但是它是一个多叉树。每一个结点都表示很多个范围,每一个结点的大小为16K,也就是一个页(一个页由4个块组成,一块是4K),每一个结点存放索引的范围 和 指向对应的下一层结点的指针;然后每层树层层划分,越往下,范围就越细,而最后的叶子结点就是我们要的数据地址的范围(注意,这是范围,而不是一个数据),注意,这里存的是数据地址,也就是磁盘的数据文件里这一个范围的数据的磁盘地址。
整体,B树是一个绝对平衡的树,B树及其所有的子树的平衡因子是0,也就是是所有子树的左右子树的高度是一样的。
查找:一开始把根节点从磁盘读到内存,然后从根节点找对应的范围,继续找到数据所以在范围的下一个结点,然后将这个结点读到内存,… … 以此类推,每一次读取到内存都是一次磁盘的IO。然后最后找到根节点,将根节点加载到内存(注意,根节点存的是数据范围),然后通过一定的算法(如二分查找),找到对应的数据,然后会有该数据对应的磁盘的数据文件里数据的地址,最后通过地址即可拿到该索引对饮管道所有数据。
2.1.2、实例
比如有这样的一张表,其中学号是主键:
因此我们接下来说一下由主键索引的这个表构建的底层的B树的结构:
2.1.2.1、结构
第一层,存的是结点索引的范围,存放的结点的范围表示的是:1、170 <= x < 174 ,2、174 <= x < 178 ,3、178 <= x 。每个结点都会指向下一层的结点。
第二层,存的是结点索引的范围,存放的结点的范围表示的是:1、170 <= x < 172 ,2、172 <= x < 174 ,3、174 <= x < 176 ,4、176 <= x < 178 , 5、178 <= x 等等。每个结点都会指向下一层的结点。
第三层:存的是数据的范围,存放的结点的范围表示的是:1、170 <= x < 171 ,2、171 <= x < 172 ,3、172 <= x < 173 等等。
每个索引结点存放:1、索引数据的范围,2、每个范围对应的下一个结点的指向
每个数据结点存放:1、索引数据,2、对应数据在磁盘的数据文件里数据地址
2.1.2.2、查找流程
比如我们查找173学号的数据。
1、先从索引文件将根节点加载到内存。
2、通过根节点发现173是 >= 170 && < 174,所以我们走最左边的指针,将最左边指向的结点加载到内存。
3、再通过加载的这个结点寻找173,发现173是 >= 172 && < 174的,所以我们走右边的指针,并将这个指向的数据结点加载到内存。
4、这时候通过二分查找,快速的找到对应的数据,然后通过数据对应的磁盘的地址,访问到数据文件里对应的数据。
2.1.3、总结
从上面的实例,我们可以看出B树的效率还是很高的,看了这个结构有些人会想:既然是多叉树,那么为什么不只用一个结点,也就是为什么不用一个结点来表示所有数据的范围呢?
原因是:如果数据量特别大,那么这个索引结点也将特别大,这个索引结点是要加载到内存的,所
以是很吃内存的,有些甚至没法加载进去。如果用一定量的结点就不会发生这种情况,我们将一个结点加载到内存后,通过这个结点找到下一个结点后,会将之前的结点从内存中删掉,然后再加载新的结点(因为之前的结点的任务已经完成了,没有用了),这样就均衡了性能和内存。
上面的例子是查找某一个数据,但是如果我们查找的是一个范围的数据呢?比如我们要查找173 ~ 175的数据,它的查找流程会变成:
1、加载根节点
2、逐级往下加载结点
3、找到173的数据
4、然后这里是麻烦的,因为174 ~ 175不在这个数据结点,所以我们得重新加载根节点,再按照上面的流程找174 ~ 175。
这个就大大增加了磁盘IO的次数,我们知道磁盘IO的效率并不高,因为这个,MYSQL后来升级了B树,称为B+树。
2.2、B+树结构
2.2.1、概念
结构:其实B+树和B树整体结构是基本相同的,B+树升级的是:1、每一层的结点都多了一个指针,这个指针指向同一层的下一个结点,这个指针就是为了提高范围查找的效率。2、B树的数据结点存的是索引数据及其对应的磁盘地址,而B+树存的是索引数据及其对应的所有数据,这也是为了提高性能,减少了磁盘的IO,虽然存的量多,但是每次只加载一个结点,前面的结点就会删除,所以其实差的不多。(B树的是非聚簇索引,B+树的是聚簇索引)。
查找:找一个数的时候,B+树的流程和B树是一样的,不同的是查找范围时候的流程。如果查找的是范围数据,会将其他数据的结点通过最后一个指针找到(而B树还需要从根节点重新找),最后找到根节点,就直接从根节点就可以拿到所有的数据(而B树还需要再一次的磁盘IO,通过磁盘地址找到对应的数据)。
2.2.2、实例
同样使用上次的表结构及其数据,不同的是树结构:
2.2.2.1、结构
B+树的结构大体和B树的结构是相同的。
第一个不同的是每一个结点后面都有一个指向本层下一个结点的指针,这个目的是为了让范围查询更加便捷;
第二个不同的是最后的根节点,也就是数据页,里面存的不再是数据的磁盘地址,而是该索引对应的所有数据。
2.2.2.2、查找流程
比如我们还是查找范围是173 ~ 175的数据:
1、先将根节点加载到内存,先去找最小数173,在>= 170 && < 174的范围。
2、将最左边指针指向的结点加载到内存,继续寻找173,在>= 172 && < 174的范围。
3、将最右边指针指向的结点加载到内存,里面只有一个173的数据页,然后通过最后的指针拿到其指向的结点,也就拿到了174 ~ 175的数据页。
4、直降从这两个数据页读取数据即可。