一 简介
MySql的索引是一种数据结构。它可以帮助数据库高效地查询、更新数据表中的数据。它类似于书籍的目录,通过指向数据行的位置,可以快速定位和访问表中的数据。
那为什么使用索引呢?
主要就是提升数据检索的效率
刚才提到索引是一种数据结构,那它选择的是哪种数据结构呢?让我们一个一个地来看一下吧
二 索引数据结构的选择
1 HASH
它的时间复杂度是O(1),感觉非常适合。但是它不支持范围查找
2 二叉搜索树
它支持范围查找,但它的时间复杂度在单边树的时候为O(N) ,因为节点个数过多时,树的高度无法保证,磁盘IO的次数就会变得很多。因为数据库中的数据都是在磁盘上保存的,每访问一次子节点,就会发生一次磁盘IO,磁盘IO是制约数据库性能的主要因素
3 N叉树
它的每个节点可以有超过两个的子节点,可以解决树高的问题。它的时间复杂度是O(logN),在数据量相同的情况下,可以有效的控制树高,也就是说可以使用更少的IO次数找到目标节点,从而提高数据库的效率。但是这个还是不够好,看看下一个(下一个更乖,哈哈)
比如:

4 B+树
B+树是一种经常用于数据库和文件系统等场合的平衡查找树,这就是MySql索引采用的数据结构。它的时间复杂度也是O(logN),可以有效的控制树高
比如:

它的特点如下:
能够保持数据稳定有序,插⼊与修改有较稳定的时间复杂度
⾮叶⼦节点仅具有索引作⽤,不存储数据,所有叶⼦节点保真实数据
所有叶⼦节点构成⼀个有序链表,可以按照key排序的次序依次遍历全部数据
上面的两个树有什么区别?(重点)
对于B+树而言:
1)叶子节点之间有一个相互连接的引用(看图),可以通过一个叶子节点找到它相近的兄弟节点。MySql在组织叶子节点时使用的是双向链表(上面的图是单向的,应该是双向的)。
2)非叶子节点的值都包含在叶子节点中。MySql非叶子节点只保存了对叶子节点的引用,并没有保存真实的数据,所有真实的数据都保存在叶子节点中。
3)对于B+树而言,在相同树高的情况下,查找任意元素的时间复杂度都一样,性能均衡
要想知道索引在整个数据检索的过程中是如何工作的,得从MySql存储结构说起:
三 MySql中的页
1 简介
在 .ibd ⽂件中最重要的结构体 就是Page(⻚),⻚是内存与磁盘交互的最⼩单元,默认⼤⼩为
16KB,每次内存与磁盘的交互⾄少读取⼀⻚,所以在磁盘中每个⻚内部的地址都是连续的,之所
以这样做,是因为在使⽤数据的过程中,将来要使⽤的数据⼤概率与当前访问的数据在空间上是临近的,所以⼀次从磁盘中读取⼀⻚的数据放⼊内存中,当下次查询的数据还在这个⻚中时就可以从内存中直接读取,从⽽减少磁盘I/O提⾼性能。
其中,那个.ibd文件就是innodb存储引擎生成的表空间文件
每个页中即使没有数据也会使用16KB的存储空间,同时与索引的B+树中的节点对应
比如:

查看页大小

详解:
在MySQL中有多种不同类型的⻚,最常⽤的就是⽤来存储数据和索引的"索引⻚",也叫做"数据
⻚",但不论哪种类型的⻚都会包含⻚头(File Header)和⻚尾(File Trailer),⻚的主体信息使⽤数
据"⾏"进⾏填充。
这里只介绍数据页(索引页),数据⻚的基本结构如下图所⽰:

独立表空间文件是每创建一张表生成一个保存数据的文件
2 页文件头和页文件尾
表示当前页文件的主要信息。这里只关注,上一页页号和下一页页号,通过这两个属性可以把页与页之间连接起来,形成一个双向链表(通过页号和页大小,可以计算出下一页和上一页在磁盘上的偏移量)

3 页主体
页主体是保存真实数据的主要区域,每当创建一个新页,都会自动分配两个行,一个是页内最小行Infimun,另一个是页内最大行 Supremun,这两个行并不存储任何真实信息,而是作为数据行链表的头和尾 ,第一个数据行有一个记录下一行的地址偏移量的区域next_record,将页内所有数据行组成了一个单向链表,新页(没有数据)结构如下:

此时,插入数据时,将Infimun连接第一个数据行,最后一行真实数据行连接Supremun,这样数据行 就成了一个单向链表,更多的行插入之后,会按照主键从小到大的顺序进行连接
图:

这是就会有疑问了:单向链表,数据过多的时候,查起来的时候也麻烦,怎么办呢?
页目录:
4 页目录
在每一个页中加入页目录的结构,将页内所有行进行分组,其中,最小行单独为一组,其他每个组最多8条数据,超过8条时会分裂出来一个新组,最大行永远在最后一个分组中。同时把每个组最后一行在页中的地址,按主键从小到大的顺序记录在页目录中,页目录中的每一个位置称为一个槽,槽的数量与分组的数据是一致的

比如查找主键为6的行:
1)找到这条记录所在的页、
2)在页中找到所在的槽
3)在分组中找到对应的记录
5 数据页头
记录了当前页保存数据相关的信息

6 B+树在索引中的应用

索引页保存的是主键的值和子节点的引用,非叶子节点记录的是索引信息
叶子节点存储的是真实数据,数据页保存的是具体数据,叶子节点最终形成了一个双向循环链表
值得注意的是,所有关于页的访问和操作都是在内存中进行的
加载索引页1 -> 加载索引页2 -> 加载数据页3 就是3次IO
OK啦!!!!