《彻底掌握索引》—— 要点问题(2)

7. 什么是回表?如何减少回表?

当查询的数据在索引树中,找不到的时候,需要回到主键索引树中去获取,这个过程叫做回表。回表查询可以理解为普通索引的查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。

在这之前,我们需要了解的就是主键索引和非主键索引在数据结构上的区别,两者使用的数据结构都是 B+Tree,唯一的区别在于叶子结点中存储的内容不同:

  • 主键索引的叶子结点存储的是一行完整的数据。
  • 非主键索引的叶子结点存储的则是主键值。叶子结点不包含行记录的全部数据;非主键的叶子结点中,除了用来排序的key还包含一个bookmark;该书签存储了聚集索引的key。

所以,当我们需要查询的时候:

  • 如果是通过主键索引来查询数据,例如 select * from user where id=100,那么此时只需要搜索主键索引的 B+Tree 就可以找到数据。

  • 如果是通过非主键索引来查询数据,例如 select * from user where username='javaboy'那么此时需要先搜索 username 这一列索引的 B+Tree,搜索完成后得到主键的值,然后再去搜索主键索引的 B+Tree,就可以获取到一行完整的数据

对于第二种查询方式而言,一共搜索了两棵 B+Tree,先搜索 B+Tree 拿到主键值后再去搜索主键索引的 B+Tree,这个过程就是所谓的回表。

从上面的分析中我们也能看出,通过非主键索引查询要扫描两棵 B+Tree,而通过主键索引查询只需要扫描一棵 B+Tree,所以如果条件允许,还是建议在查询中优先选择通过主键索引进行搜索

举个例子:

create table `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '名称',
  `age` int(3) unsigned NOT NULL DEFAULT '1' COMMENT '年龄',
  primary key (`id`),
  KEY `I_name` (`name`)
) ENGINE=InnoDB;# 新增数据
INSERT INTO student (name, age) VALUES("小赵", 10),("小王", 11),("小李", 12),("小陈", 13);# 进行数据的指数增长
INSERT INTO student (name, age) SELECT `NAME`, age FROM student;

每一个索引在 InnoDB 里面对应一棵B+树,那么此时就存着两棵B+树:
在这里插入图片描述
可以发现区别在与叶子节点中,主键索引存储了整行数据,而非主键索引中存储的值为主键id,在我们执行如下sql:

SELECT age FROM student WHERE name = '小李'

执行流程如下:

  1. 在name索引树上找到名称为小李的节点 id为 03。
  2. 从id索引树上找到id为 03的节点 获取所有数据。
  3. 从数据中获取字段命为age的值返回 12。

在流程中从非主键索引树搜索回到主键索引树搜索的过程称为:回表。

优化的手段:
5. 尽量使用主键索引查询
6. 引入覆盖索引。
7. 索引下推

接下来会详细说一下这几种方式。

8. 什么是覆盖索引?

8.1 定义

就是把单列的非主键索引修改为多字段的联合索引。在一棵索引数上,就找到了想要的数据, 不需要去主键索引树上,再检索一遍 这个现象,称之为索引覆盖
也就是说覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

8.2 如何使用覆盖索引?

在前面我们已经建立了表student,那么现在要求根据名称获取学生的年龄,并且该搜索场景非常频繁,那么先在我们删除掉之前以字段name建立的普通索引,以name和age两个字段建立联合索引,sql命令与建立后的索引树结构如下:

# 查询当前表中的索引
show index from student;
# 删除之前的非主键索引
alter table student drop index I_name;
# 添加非主键索引
alter table student add index I_name_age(name, age);

在这里插入图片描述
我们再次执行如下sql:

select age from student where name = '小李'

具体指定流程如下:

  • 在name,age联合索引树上找到名称为小李的节点。
  • 此时节点索引(非主键索引)里包含信息age 直接返回 12。
8.3 如何确定数据库成功使用了覆盖索引呢?

当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息:
在这里插入图片描述
这里我们很清楚的看到Extra中using index表明我们成功使用了覆盖索引。

要注意的是:不使用主键索引,不一定就需要回表,比如查询的列本身就存在于索引中,那么即使使用二级索引,一样也是不需要回表的。

9. 索引最左前缀原则

即当你创建了一个联合索引,该索引的任何最左前缀都可以用于查询。比如当你有一个联合索引 (col1, col2, col3),该索引的所有前缀为 (col1)、(col1, col2)、(col1, col2, col3),包含这些列的所有查询都会使用该索引进行查询。

但是要注意的是:
有时候并不一定会遵守最左前缀原则,MySQL8.0版本开始增加了索引跳跃扫描的功能,当第一列索引的唯一值较少时,即使where条件没有第一列索引,查询的时候也可以用到联合索引。

CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) NOT NULL COMMENT '姓名',
  `gender` tinyint NOT NULL COMMENT '性别',
  PRIMARY KEY (`id`),
  KEY `idx_gender_name` (`gender`,`name`)
) ENGINE=InnoDB COMMENT='用户表';

在性别和姓名两个字段上(gender,name)建立联合索引,性别字段只有两个枚举值。

执行SQL查询验证一下:

explain select * from user where name='小明';

在这里插入图片描述
虽然SQL查询条件只有name字段,但是从执行计划中看到依然是用了联合索引。
并且Extra列中显示增加了Using index for skip scan,表示用到了索引跳跃扫描的优化逻辑。就是匹配的时候遇到第一列索引就跳过,直接匹配第二列索引的值,这样就可以用到联合索引了。

我们优化一下SQL,把第一列的所有枚举值加到where条件中,也可以用到联合索引:

select * from user where gender in (0,1) and name='小明';
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
0; } ``` 程序运行时,显示一个菜单,让用户选择不同的功能,例如: ``` 1位图索引(Bitmap Index)是一种特殊类型的索引,它使用位图存储数据。对于列中的每个不同值,位图索引创建一个位图,其中每个位对应于表中的. 添加学生信息 2. 显示所有学生信息 3. 按总分排序 4. 按DataStructure成绩排序一行。如果值出现在行中,则相应的位设置为1,否则为0。通过使用位图索 5. 按C成绩排序 6. 查找某一C成绩的记录 0. 退出程序 请选择操作引,可以快速地定位符合特定查询条件的行。 位图索引通常用于低基数(即不同值数量较少)列上,例如性别列等。在这种情况下,位图索引可以(输入数字): ``` 用户可以根据需要选择不同的功能,程序根据用户的选择执行相应极大地减少磁盘 I/O 操作,提高查询性能。但是,如果列具有高基数(即的操作,例如: ``` 请输入学号:001 请输入姓名:张三 请输入DataStructure成绩:90 请输入不同值数量较多),则位图索引的效率可能降低。 在 Oracle 数据库中,可以使用 CREATEC成绩:80 添加成功 请选择操作(输入数字):2 学号 姓名 DataStructure C 总分 BITMAP INDEX 语句创建位图索引,例如: ``` CREATE BITMAP INDEX idx_gender ON employees(gender); ``` 001 张三 90.0 80.0 170.0 请选择操作(输入数字):3 按总这将在 employees 表的 gender 列上创建一个位图索引。在查询中使用位图索引时,可以使用 BIT分排序结果: 学号 姓名 DataStructure C 总分 001 张三 90.0 80.0 MAP AND、BITMAP OR、BITMAP NOT 等操作符组合多个位图索引,以获得更高效的查询性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术闲聊DD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值