当前位置: 首页 > article >正文

在Mysql中,什么是回表,什么是覆盖索引,索引下推?

一、什么是回表查询?

通俗的讲就是,如果索引的列在 select 所需获得的列中(因为在 mysql 中索引是根据索引列的值进行排序的,所以索引节点中存在该列中的部分值)或者根据一次索引查询就能获得记录就不需要回表,如果 select 所需获得列中有大量的非索引列,索引就需要到表中找到相应的列的信息,这就叫回表。

InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:

(1)如果表定义了主键,则PK就是聚集索引;
(2)如果表没有定义主键,则第一个非空唯一索引(not NULL unique)列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

先创建一张表,sql 语句如下:

create table xttblog(
    id int primary key, 
    k int not null, 
    name varchar(16),
    index (k)
)engine = InnoDB;

然后,我们再执行下面的 SQL 语句,插入几条测试数据。

INSERT INTO xttblog(id, k, name) VALUES(1, 2, 'xttblog'),
    (2, 1, '业余草'),
    (3, 3, '业余草公众号');

假设,现在我们要查询出 id 为 2 的数据。那么执行 select * from xttblog where ID = 2; 这条 SQL 语句就不需要回表。原因是根据主键的查询方式,则只需要搜索 ID 这棵 B+ 树。主键是唯一的,根据这个唯一的索引,MySQL 就能确定搜索的记录。

但当我们使用 k 这个索引来查询 k = 2 的记录时就要用到回表。select * from xttblog where k = 2; 原因是通过 k 这个普通索引查询方式,则需要先搜索 k 索引树,然后得到主键 ID 的值为 1,再到 ID 索引树搜索一次。这个过程虽然用了索引,但实际上底层进行了两次索引查询,这个过程就称为回表。

也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。

我这里表里的数据量比较少,如果数据量大的话,你能很明显的看出两次查询所用的时间,很明显使用主键查询效率更高。

更多如下图:
在这里插入图片描述

(1)先通过普通索引定位到主键值id=5;
(2)在通过聚集索引定位到行记录;

这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低

小总结

使用聚集索引(主键或第一个唯一索引)就不会回表,普通索引就会回表。

二、什么是索引覆盖?

只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

explain的输出结果Extra字段为Using index时,能够触发索引覆盖。

三、如何实现索引覆盖?

1、常见的方法是:将被查询的字段,建立到联合索引里去。
例子

create table user (
id int primary key,
name varchar(20),
sex varchar(5),
index(name)
)engine=innodb;

第一个sql:

select id,name from user where name='shenjian';

在这里插入图片描述
能够命中name索引,索引叶子节点存储了主键id,通过name的索引树即可获取id和name,无需回表,符合索引覆盖,效率较高。

Extra:Using index。

第二个sql:

select id,name,sex from user where name='shenjian';

在这里插入图片描述

能够命中name索引,索引叶子节点存储了主键id,没有储存sex,sex字段必须回表查询才能获取到,不符合索引覆盖,需要再次通过id值扫描聚集索引获取sex字段,效率会降低。

Extra:Using index condition。

如果把(name)单列索引升级为联合索引(name, sex)就不同了。


create table user1 (
id int primary key,
name varchar(20),
sex varchar(5),
index(name, sex)
)engine=innodb;

在这里插入图片描述
可以看到:

select id,name … where name=‘shenjian’;
select id,name,sex … where name=‘shenjian’;

单列索升级为联合索引(name, sex)后,索引叶子节点存储了主键id,name,sex,都能够命中索引覆盖,无需回表。

画外音,Extra:Using index。

四、哪些场景可以利用索引覆盖来优化SQL?

场景1:全表count查询优化

在这里插入图片描述
原表为:
user(PK id, name, sex);

直接:
select count(name) from user;
不能利用索引覆盖。

添加索引:
alter table user add key(name);
就能够利用索引覆盖提效。

场景2:列查询回表优化

这个例子不再赘述,将单列索引(name)升级为联合索引(name, sex),即可避免回表。

场景3:分页查询

将单列索引(name)升级为联合索引(name, sex),也可以避免回表。

五、什么是索引下推

索引下推(Index condition pushdown)简称ICP,是一种优化数据库查询的技术,它利用了数据库索引的特性,在一定条件下,在索引层面就过滤掉不需要的数据,从而减少查询时需要访问的数
据块,提高查询效率。

在普通的查询中,数据库需要先从表中读取所有的数据记录,然后再根据查询条件过滤不需要的记录,最后返回查询结果。而在索引下推中,数据库会在索引树的节点上进行条件过滤,只将满足条件的数据块返回,而不是读取整个数据记录。这样可以避免从磁盘读取不必要的数据,降低IO开销,提升查询速度。

索引下推的主要优点是减少了回表操作,即减少了访问磁盘的次数和需要传输的数据量,从而提高了查询效率和响应速度。具体来说,如果查询条件涉及到的字段都可以通过索引直接获取,而不需要回表操作,那么查询速度将大大提高。

需要注意的是,索引下推并不是适用于所有类型的查询,它涉及到查询中所使用的索引类型和查询条件的限制。通常,只有涉及到等值查询或范围查询的情况下,才能使用索引下推技术实现优化。同时,索引下推也会产生额外的开销,需要消耗更多的CPU资源,因此需要在实际应用中进行评估和优化。

MySQL的大概框架为:
在这里插入图片描述
索引下推的下推其实就是指将部分上层(服务层)负责的事情,交给了下层(引擎层)去处理。

假设有这样一个用户表:
在这里插入图片描述

创建一个联合索引(age, birthday),并查询出年龄>20,且生日为03-01的用户:

select * from user where age>20 and birthday=03-01

为在没有索引下推的情况下,执行步骤如下:

  • 存储引擎根据索引查找出age>20的用户id,分别是:4,5,7
  • 存储引擎到表格中取出id in (4,5,7)的3条记录,返回给服务层
  • 服务层过滤掉不符合birthday="03-01"条件的记录,最后返回查询结果为id=4的1行记录。

如果开启了索引下推优化,执行步骤如下:

  • 存储引擎根据索引查找出age>20的用户id,并使用索引中的birthday字段过滤掉不符合birthday="03-01"条件的记录,最后得到id=4;
  • 存储引擎到表格中取出id=4的1条记录,返回给服务层;
  • 服务层过滤掉不符合birthday="03-01"条件的记录,最后返回查询结果为id=4的1行记录。
  • 启用索引下推后,把where条件由MySQL服务层放到了存储引擎层去执行,带来的好处就是存储引擎根据id到表格中读取数据的次数变少了。在上面这个例子中,没有索引下推时需要多回表查询2次。并且回表查询很可能是离散IO,在某些情况下,对数据库性能会有较大提升。

假设有这么个需求,查询表中“名字第一个字是张,性别男,年龄为10岁的所有记录”。那么,查询语句是这么写的

mysq> select * from tuser where name like '张 %' and age=10 and ismale=1;

根据前面说的“最左前缀原则”,该语句在搜索索引树的时候,只能匹配到名字第一个字是‘张’的记录(即记录ID3),接下来是怎么处理的呢?当然就是从ID3开始,逐个回表,到主键索引上找出相应的记录,再比对age和ismale这两个字段的值是否符合。

但是!MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表字数。
下面图1、图2分别展示这两种情况。

在这里插入图片描述
在这里插入图片描述
图 1 中,在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。

图 2 跟图 1 的区别是,InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在我们的这个例子中,只需要对 ID4、ID5 这两条记录回表取数据判断,就只需要回表 2 次。

总结

如果没有索引下推优化(或称ICP优化),当进行索引查询时,首先根据索引来查找记录,然后再根据where条件来过滤记录;在支持ICP优化后,MySQL会在取出索引的同时,判断是否可以进行where条件过滤再进行索引查询,也就是说提前执行where的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。


http://www.kler.cn/a/149546.html

相关文章:

  • jwt用户登录,网关给微服务传递用户信息,以及微服务间feign调用传递用户信息
  • 哪款开放式耳机好用?5款实力出众的开放式耳机按头安利!
  • Node.Js+Knex+MySQL增删改查的简单示例(Typescript)
  • Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
  • Java Stream 流常用操作大全
  • 智能零售柜商品识别
  • Qt 软件调试(一) Log日志调试
  • MapReduce概念
  • 简化文件上传流程:学习如何封装Vue2拖拽上传组件
  • 4.ORB-SLAM3中如何实现稠密建图(二):稠密建图如何控制三大线程与稠密建图代码解析
  • 额,收到阿里云给的赔偿了!
  • OpenCV | 傅里叶变换——低通滤波器与高通滤波器
  • 西南科技大学C++程序设计实验二(类与对象一)
  • 做到这一点,运维可高枕无忧
  • 读天下杂志读天下杂志社读天下编辑部简介
  • 王者荣耀游戏制作
  • 从零构建属于自己的GPT系列2:预训练中文模型加载、中文语言模型训练、逐行代码解读
  • During handling of the above exception, another exception occurred解决方案
  • vue项目实现生成一个简单二维码
  • 前端面试灵魂提问
  • 浅析智慧社区建设趋势及AI大数据监管平台方案设计
  • wsj0数据集原始文件.wv1.wv2转换成wav文件
  • Kanna库编写数据抓取代码示例
  • C# 线程(1)
  • 分布式运用之ELK企业级日志分析系统
  • 【C 语言经典100例】C 练习实例14 - 将一个正整数分解质因数