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

mysql<回表,覆盖索引,最左匹配,索引下推>

1.两类索引

MySQL 中的索引有很多中不同的分类方式,可以按照数据结构分,可以按照逻辑角度分,也可以按照物理存储分,其中,按照物理存储方式,可以分为聚簇索引和非聚簇索引。

主键索引,其实就是聚簇索引(Clustered Index);主键索引之外,其他的都称之为非主键索引,非主键索引也被称为二级索引(Secondary Index),或者叫作辅助索引。

对于主键索引和非主键索引,使用的数据结构都是 B+Tree,唯一的区别在于叶子结点中存储的内容不同:

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

这就是两者最大的区别。

查询的时候:

  1. 如果是通过主键索引来查询数据,例如 select * from user where id=100,那么此时只需要搜索主键索引的 B+Tree 就可以找到数据。
  2. 如果是通过非主键索引来查询数据,例如 select * from user where username='javaboy',那么此时需要先搜索 username 这一列索引的 B+Tree,搜索完成后得到主键的值,然后再去搜索主键索引的 B+Tree,就可以获取到一行完整的数据。(对username创建普通索引,由于是select*查询的是全部的数据,就会在叶子节点再次走可key的b+tree)

 

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

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

2.建立一张学生表,其中包含字段id设置主键索引、name设置普通索引、age(无处理),并向数据库中插入4条数据:("小赵", 10)("小王", 11)("小李", 12)("小陈", 13)

 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);
         

        

每一个索引在 InnoDB 里面对应一棵B+树,那么此时就存着两棵B+树。

 

可以发现区别在与叶子节点中主键索引存储了整行数据,而非主键索引中存储的值为主键id, 在我们执行如下sql后

SELECT age FROM student WHERE name = '小李';

 sql执行流程为:

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

在流程中从非主键索引树搜索回到主键索引树搜索的过程称为:回表,在本次查询中因为查询结果只存在主键索引树中,我们必须回表才能查询到结果,那么如何优化这个过程呢?引入正文覆盖索引

2.覆盖索引

就是把单列的非主键 索引 修改为 多字段 的联合索引, 在一棵索引数上。 就找到了想要的数据, 不需要去主键索引树上,再检索一遍 这个现象,称之为 索引覆盖.

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

  • 如何使用是覆盖索引?

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

 # 删除之前的非主键索引
alter table student drop index I_name;
 # 添加非主键索引
alter table student add index I_name_age(name, age);
         

 那在我们再次执行如下sql后:

select age from student where name = '小李';

流程为:

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

当发起一个索引覆盖查询时,在explainextra列可以看到using index的信息: 

这里我们很清楚的看到Extrausing index表明我们成功使用了覆盖索引。

覆盖索引避免了回表现象的产生,从而减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是性能优化的一种手段。

3.索引下推

在新版本中,在存储引擎中就会过滤掉一部分数据

老版本中,是在存储引擎中先取出来再在server中进行过滤

询表中“名字第一个字是张,性别男,年龄为10岁的所有记录”。

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

该语句在搜索索引树的时候,只能匹配到名字第一个字是‘张’的记录(即记录ID3),接下来是怎么处理的呢?

当然就是从ID3开始,逐个回表,到主键索引上找出相应的记录,再比对ageismale这两个字段的值是否符合。

但是!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的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。

 

相关文章:

  • 基于Python的电子病历实体识别系统
  • 计算机视觉项目-人脸识别与检测
  • Nature全球潮汐可视化兼影像数据下载网站:Intertidal change
  • 每日优鲜深陷“破产风波”,生鲜电商路在何方?
  • 需求分析的基本任务 ,需求分析参与人 ,目前用于需求分析的结构化分析方法遵守的准则,确定对系统的综合要求及案例
  • Python每日一练(牛客数据分析篇新题库)——第31天:中位函数
  • 电源硬件设计----电源基础知识(2)
  • 【魔方代码】1200行C语言代码实现“魔方”程序,学会它买魔方的钱都省了,拿走不谢~
  • 4、乐趣国学—“满招损,谦受益。”
  • 商城项目07_网关工程初步搭建、商品分类树状结构展示、网关配置、解决跨域问题
  • 【Python刷题篇】——Python入门 09 字典(下)
  • MySQL-查询数据库(二)
  • 《安富莱嵌入式周报》第281期:Keil Studio发布VSCode插件,微软嵌入式IDE升级,开源穿戴手表,CAN XL汽车单片机,USB4 V2.0规范
  • 【数据挖掘】pandas使用手册
  • 图像处理技术的综合应用——提取篮球
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 10个最佳ES6特性 ES7与ES8的特性
  • Apache的80端口被占用以及访问时报错403
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CentOS6 编译安装 redis-3.2.3
  • go append函数以及写入
  • HashMap ConcurrentHashMap
  • HTTP 简介
  • IndexedDB
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JAVA 学习IO流
  • JavaScript类型识别
  • JS+CSS实现数字滚动
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Netty源码解析1-Buffer
  • nodejs实现webservice问题总结
  • 从零开始在ubuntu上搭建node开发环境
  • 浏览器缓存机制分析
  • 使用putty远程连接linux
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 在electron中实现跨域请求,无需更改服务器端设置
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (C)一些题4
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (第一天)包装对象、作用域、创建对象
  • (动态规划)5. 最长回文子串 java解决
  • (二)windows配置JDK环境
  • (三)终结任务
  • (一)Thymeleaf用法——Thymeleaf简介
  • (一)UDP基本编程步骤
  • (转)树状数组
  • (转)项目管理杂谈-我所期望的新人
  • (转)重识new
  • *2 echo、printf、mkdir命令的应用
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net core 6 redis操作类
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法