MySql InnoDB索引实现原理,带你彻底搞懂索引数据结构

先来了解一个概念:Innodb_page_size,这是Innodb在存取数据时,最小的基本单位,可以理解为"一页",默认大小为16KB,Innodb每次向磁盘存取数据时,最小存取一页数据,即16KB数据,这样做的好处是:可以有效减少IO操作,提高性能;

先来看一眼 “页” 的结构:
在这里插入图片描述
看上去可能有点儿蒙,没关系,暂时只需要知道有这么个东西,由图可知,“页”中包含“页目录”和“用户数据区域”;

先来举个例子,建立一张表,如下图:
在这里插入图片描述
此时,这张表的 “页” 为:
在这里插入图片描述
可以看到“页目录”和“用户数据区域”是没任何数据的;
现在往表里随便插入几条数据,由于MySql对于新插入的数据,会默认按照主键进行排序,用来提高查询性能,所以此时,这张表的 “页” 为:

在这里插入图片描述此时新插入的数据,会被保存到“用户数据区域”;
接着执行一条查询语句:
select * from t1 where a = 3;
会扫描整个 “用户数据区域”,性能显然不是最优的;

接下来解释一下图中的“页目录”,“页目录”可以理解为书本中的目录,“用户数据区域”可以理解为书本中的正文;

假设此时一共插入了四条数据,可以把这四条数据按照主键的顺序,分成两组:
在这里插入图片描述
此时,“页目录”里记录的,是每组数据里,最小主键值 和 指向当前组的指针,如下图:
在这里插入图片描述
接着执行一条查询语句:
select * from t1 where a = 3;
此时可以通过“页目录”,判断 a=3 只可能在第一组,此时,只需要遍历第一组中每条数据即可,这样查询性能就会提升;

按照上面了解的,假设现在表中数据很多,即很多“页”数据,如下图:
在这里插入图片描述
接着执行一条查询语句:
select * from t1 where a = 3;
会挨个遍历每个“页”中的“页目录”,直到匹配到数据为止,性能显然不是最优的;

此时,可以参照前面介绍的“页目录”的思想,再来重新开辟一个新的“页”,专门用来存储每一“页”内,“页目录”中最小的值 和 指向这一页的指针,如下图:
在这里插入图片描述
接着执行一条查询语句:
select * from t1 where a = 3;
可以直接确定 a=3 只可能在第一页内,到此,性能又有所提升;

如果把刚刚上面的图画规范一点,就变成了下面这张图:
在这里插入图片描述
由上图可见,下面那两页,是专门存放数据的,顶部那一页,是专门存放下面每个页地址的;

到这里,细心的同学可能会发现,这不就是一棵B+Tree嘛?如果没看出来也没关系,接着看下面这张图:
在这里插入图片描述
把每一页中的“页目录”提出来,放在中间的位置,这就是InnoDB中B+Tree数据结构;

由图可知,B+Tree 每个节点左边的数据都比自己小,右边的数据都比自己大,即天生有序,另外,每个节点里面,可以有多个元素,非叶子节点,冗余了一部分主键数据;

接下来结合上图,解释一些名词,上面这棵树,按照主键进行排序,称为主键索引,叶子节点存储的是整张表的具体数据,又叫聚集索引,即索引和表中数据同时在一起;

再观察,查询某条数据时,只有两条路径,要么是从左往右依次遍历每个叶子节点,要么是从上往下通过页目录最终匹配到数据。

如何判断查询数据时,有没有走索引呢?
如果查询是从左往右依次遍历叶子节点,就是全表扫描,如果查询是从上往下,就是走索引,通过explain执行sql时可查看是否走了索引;

再来解释下每个叶子节点之间的双向指针,其实是专门用来做范围查找,举个例子,执行一条查询语句:
select * from t1 where a > 3;
由于a是主键,InnoDB会先从主键索引中从上到下找到a=3的叶子节点,由于叶子是有序的,并且相互之间有指针指向前后节点,如果此时a > 3,那就从a=3这个节点开始,往后的数据全部匹配,如果查的是a < 3,那就从a=3这个节点开始,往前的数据全部匹配;

现在建一个联合索引:
create index idx on t1(b,c,d);
InnoDB会按照b,c,d三个字段排序,建立一棵B+Tree,如下图:
在这里插入图片描述
解释一下,叶子节点存储了按照b,c,d排好序后的全表数据,非叶子节点只存储了b,c,d字段的值;
细心的同学可能发现,新创建的索引又把全表数据复制保存了一份,太浪费资源了,这里的叶子节点数据其实和主键索引中叶子节点数据是一样的,只是顺序不同而已,所以需要把叶子节点冗余的数据去掉,只保存b,c,d字段的值,如下图:
在这里插入图片描述
但是这样会有一个缺点,如果查询b,c,d之外的字段,该如何查呢?
此时可以在叶子节点,记录每条数据主键的值,如下图:
在这里插入图片描述
由图可知,根据b,c,d找到主键后,拿着主键到主键索引上去找,最终找到具体数据,这个过程就叫做:回表;
此时这个索引没有和完整数据关联在一起,只关联了主键值,又叫非聚集索引;

回表过程如下:
在这里插入图片描述
左边是非主键索引,右边是主键索引;

最左匹配原则:此时有联合索引b,c,d,如果想使用这个联合索引,查询顺序必须按照b,c,d顺序来,即:b、bc、bcd,如果不连续时,比如:bd,只用到了b列的索引,c列和d列都没有用到。

select * from t1 where c = 1 and d = 3;
由于联合索引树每个节点都是按照b,c,d排序的,此时没有给b的值,从联合索引树从上往下查找时,就确定不了该从哪里开始往下查找,所以只能走全表扫描;

select * from t1 where b = 1;
此时走索引,只匹配b的值,从上往下查找;

select * from t1 where b > 1;
此时会走全表扫描,原因是如果走索引,每条记录都会回表,会回表多次,还不如一次全表扫描来的快,InnoDB底层会判断要走索引还是全表扫描;

select b,c,d,a from t1 where b > 1;
此时会走索引,因为联合索引叶子节点已经保存了b,c,d的值和主键值,不需要回表;

暂时先记录这么多!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值