InnoDB 的B+树索引原理

一、InnoDB原理

在 InnoDB 中,每个索引其实都是一颗 B+ 树,主键索引称作聚簇索引,其他非主键索引称作二级索引,每个表中每一行的记录值都完整的保存在主键索引的叶子节点上,二级索引的叶子节点保存的是主键的值

每个表至少都有一个主键索引,而且表中所有的数据行都是存放在主键索引这个 B+ 树的叶子节点上的。如果你给表的其他字段加了索引的话,这个索引就是二级索引了,二级索引也是 B+ 树。

二级索引和主键索引的不同之处在于其叶子节点上保存的值不一样,表中所有字段的值都被完整的保存在主键索引的叶子节点上,但是二级索引的叶子节点只保存对应主键的值。一个表上有多少个索引,其实就会有多少个 B+ 树。

我们举一个具体的例子来还原下这个问题。首先提供一个表,表中有三个字段 (id,k,m),分别给主键 id 和字段 k 建立主键索引和二级索引。

mysql> create table t(
id int primary key, 
k int not null, 
m int(11),
index (k)) engine=InnoDB;

然后再给表中插入几条数据,用R1、R2、R3、R4、R5表示,插入的具体数据如下:R1~R5 的 (id,k,m) 值分别为 (100,1,1000)、(200,2,2000)、(300,3,3000)、(500,5,5000)、(600,6,6000)。

主键索引叶子节点上保存完整的整行记录值,二级索引叶子节点保存主键的值,所以上面这个表 t 的数据在 mysql 底层的存储就如下示意图。

img

表 t 一共有 3 个字段,字段 m 上没有索引,也就是说表 t 上有两个索引,所以对应有 2 个 B+ 树,一个表上有多少个索引,其实就会有多少个 B+ 树。

接下来再来看下有索引和没有索引的查询区别。

比如下面这条 sql 语句,显然没有可用的索引,所以只能走全表扫描了,即把主键索引上的叶子节点从头到尾都扫描一遍,然后每扫描到一行把字段 m 的值拿出来再比对一下,筛选出满足条件的记录,这个查询是非常低效的。

 select * from t where m > 1000 and m < 3000;

再来看另一条 sql 语句,这个语句可以使用索引 k,所以该查询会先到二级索引 k 这个 B+ 树上,快速找到满足要求的叶子节点,而这里的叶子节点上只保存了主键的值,所以还需要通过获得的主键 ID 值再回到主键索引上查出所有字段的值,这个过程称作回表

select * from t where k > 3 and k < 6;

这个回表过程还可以再优化的,就是利用覆盖索引

二、覆盖索引

如果只遍历一次 B+ 树就能获取到我们要的数据,即没有回表过程,这个效率显然是不错的,这就是覆盖索引的优势。下面看个具体的例子。

mysql> create table user(
id int(11) primary key, 
name varchar(20) not null, 
age int(11),
sex int(11),
index (age)) engine=InnoDB;

依然是新建一个表,创建索引,插入一些测试数据,注意这里只是为了解释说明覆盖索引,并不表示 mysql 的真实执行方式,因为会涉及到 mysql 的优化器机制,这里暂且不说了。

1    bob 16  1
2    kom 19  0
3    gum 18  1
4    tt  20  1
5    yy  25  1

创建一个 user 表,给 age 字段添加一个二级索引,并插入上面五条数据,然后看下面这条查询语句。

select name from user where age between 18 and 21

1、age 字段上有索引,mysql 会先到 age 字段的 B+ 树上找到满足条件的第一个叶子节点(age=19),这个叶子节点上保存了对应主键 id 的值 2,然后再到主键索引上找到 id 为 2 的这条记录,同时把 name 字段拿出来。

2、重复第一步的操作,继续从 age 索引上的叶子节点往后遍历找出满足条件的第二个叶子节点,同样回到主键上拿出 name 字段的值,直到遍历到不满足条件的叶子节点(age=25)。

也就是说,这条 sql 语句虽然用到了索引,但是 age 索引上并没有要查询的 name 字段,所以只能回表到主键索引上查出 name 字段,所以这个过程其实是遍历了个两个 B+ 树。

那么我们删除 age 这个单列索引,创建一个覆盖索引 (age,name), 把要查询的 name 字段也添加到索引中来。

#删除原索引
drop INDEX age on USER
#新建覆盖索引
ALTER TABLE USER add index age_name(age,name)

由于现在这个覆盖索引上的字段包含了要查询的 age 和 name 字段,免去了到主键索引上查询数据的过程,其实也就是只遍历了一个 B+ 树,可以大大提升查询效率。

总之,在设计索引或者优化 sql 语句的时候,要尽量避免回表操作,所以使用覆盖索引是一种常用的 sql 优化手段。

所以我们平时写 sql 语句的时候,select 后面只写查询需要用到的字段,去掉不需要的字段,避免回表操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值