从头带你捋一遍 MySQL 索引结构


从头带你捋一遍 MySQL 索引结构

文章插图

我会谈谈对于索引结构我自己的看法,以及分享如何从零开始一层一层向上最终理解索引结构 。
从一个简单的表开始createtableuser(idintprimarykey,ageint,heightint,weightint,namevarchar(32))engine=innoDb;相信只要入门数据库的同学都可以理解这个语句,我们也将从这个最简单的表开始,一步步地理解MySQL的索引结构 。
首先,我们往这个表中插入一些数据 。
INSERTINTOuser(id,age,height,weight,name)VALUES(2,1,2,7,'小吉');INSERTINTOuser(id,age,height,weight,name)VALUES(5,2,1,8,'小尼');INSERTINTOuser(id,age,height,weight,name)VALUES(1,4,3,1,'小泰');INSERTINTOuser(id,age,height,weight,name)VALUES(4,1,5,2,'小美');INSERTINTOuser(id,age,height,weight,name)VALUES(3,5,6,7,'小蔡');我们来查一下,看看这些数据是否已经放入表中 。
select*fromuser;
从头带你捋一遍 MySQL 索引结构

文章插图
 
可以看到,数据已经完整地放到了我们创建的user表中 。
但是不知道大家发现了什么没有,好像发生了一件非常诡异的事情,我们插入的数据好像乱序了…
MySQL好像悄悄的给我们按照id排了个序 。
为什么会出现MySQL在我们没有显式排序的情况下,默默帮我们排了序呢?它是在什么时候进行排序的?
页的引入不知道大家毕业多长时间了,作为一个刚学完操作系统不久的学渣,页的概念依旧在脑中还没有变凉 。其实MySQL中也有类似页的逻辑存储单位,听我慢慢道来 。
在操作系统的概念中,当我们往磁盘中取数据,假设要取出的数据的大小是1KB,但是操作系统并不会只取出这1kb的数据,而是会取出4KB的数据,因为操作系统的一个页表项的大小是4KB 。
那为什么我们只需要1KB的数据,但是操作系统要取出4KB的数据呢?
这就涉及到一个程序局部性的概念,具体的概念我背不清了,大概就是“一个程序在访问了一条数据之后,在之后会有极大的可能再次访问这条数据和访问这条数据的相邻数据”
所以索性直接加载4KB的数据到内存中,下次要访问这一页的数据时,直接从内存中找,可以减少磁盘IO次数,我们知道,磁盘IO是影响程序性能主要的因素,因为磁盘IO和内存IO的速度是不可同日而语的 。
或许看完上面那一大段描述,还是有些抽象,所以我们索性回到数据库层面中,重新理解页的概念 。
抛开所有东西不谈,假设还是我们刚才插入的那些数据,我们现在要找id = 5的数据
依照最原始的方式,我们一定会想到的就是——遍历,没错,这也是我们刚开始学计算机的时候最常用的寻找数据的方式 。
那么我们就来看看,以遍历的方式,我们找到id=5的数据,需要经历几次磁盘IO 。
首先,我们得先从id=1的数据开始读起,然后判断是否是我们需要的数据,如果不是,就再取id=2的数据,再进行判断,循环往复 。
 
毋庸置疑,在MySQL帮我们排好序之后,我们需要经历五次磁盘IO,才能将5号数据找到并读出来 。
那么我们再来看看引入页的概念之后,我们是如何读数据的 。
在引入页的概念之后,MySQL会将多条数据存在一个叫“页”的数据结构中,当MySQL读取id=1的数据时,会将id=1数据所在的页整页读到内存中,然后在内存中进行遍历判断
 
由于内存的IO速度比磁盘高很多,所以相对于磁盘IO,几乎可以忽略不计,那么我们来看看这样读取数据我们需要经历几次磁盘IO(假设每一页可以存4条数据) 。
 
那么我们第一次会读取id=1的数据,并且将id=1到id=4的数据全部读到内存中,这是第一次磁盘IO,第二次将读取id=5的数据到内存中,这是第二次磁盘IO 。所以我们只需要经历2次磁盘IO就可以找到id=5的这条数据 。
但其实,在MySQL的InnoDb引擎中,页的大小是16KB,是操作系统的4倍,而int类型的数据是4个字节,其它类型的数据的字节数通常也在4000字节以内,所以一页是可以存放很多很多条数据的,而MySQL的数据正是以页为基本单位组合而成的 。
从头带你捋一遍 MySQL 索引结构

文章插图
 
上图就是我们目前为止所理解的页的结构,他包含我们的多条数据,另外,MySQL的数据以页组成,那么它有指向下一页的指针和指向上一页的指针 。


推荐阅读