我的编程空间,编程开发者的网络收藏夹
学习永远不晚

MySQL 回表

短信预约 信息系统项目管理师 报名、考试、查分时间动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

MySQL 回表

MySQL 回表

MySQL 回表

 

    五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。

 

一、简述

回表,顾名思义就是回到表中,也就是先通过普通索引扫描出数据所在的行,再通过行主键ID 取出索引中未包含的数据。所以回表的产生也是需要一定条件的,如果一次索引查询就能获得所有的select 记录就不需要回表,如果select 所需获得列中有其他的非索引列,就会发生回表动作。即基于非主键索引的查询需要多扫描一棵索引树

二、InnoDB 引擎有两大类索引

要弄明白回表,首先得了解 InnoDB 两大索引,即聚集索引 (clustered index)和普通索引(secondary index)。

聚集索引 clustered index

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

  • 如果表定义了主键,则Primary Key 就是聚集索引;
  • 如果表没有定义主键,则第一个非空唯一索引(Not NULL Unique)列是聚集索引;
  • 否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

普通索引(secondary index

普通索引也叫二级索引,除聚簇索引外的索引都是普通索引,即非聚簇索引。

InnoDB的普通索引叶子节点存储的是主键(聚簇索引)的值,而MyISAM的普通索引存储的是记录指针。

三、回表示例

数据准备

先创建一张表  t_back_to_table ,表中id 为主键索引即聚簇索引,drinker_id为普通索引。

CREATE TABLE t_back_to_table (

id INT PRIMARY KEY,

drinker_id INT NOT NULL,

drinker_name VARCHAR ( 15 ) NOT NULL,

drinker_feature VARCHAR ( 15 ) NOT NULL,

INDEX ( drinker_id )

) ENGINE = INNODB; 

再执行下面的 SQL 语句,插入四条测试数据。

INSERT INTO t_back_to_table ( id, drinker_id, drinker_name, drinker_feature )

VALUES

( 1, 2, "广西-玉林", "喝到天亮" ),

( 2, 1, "广西-河池", "白酒三斤半啤酒随便灌" ),

( 3, 3, "广西-贵港", "喝到晚上" ),

( 4, 4, "广西-柳州", "喝酒不吃饭" );

NO回表case 

使用主键索引id查询出id 3 的数据。

EXPLAIN SELECT * FROM t_back_to_table WHERE id = 3;

执行 EXPLAIN SELECT * FROM t_back_to_table WHERE id = 3,这条 SQL 语句就不需要回表。

因为是根据主键的查询方式,则只需要搜索 ID 这棵 B+ ,树上的叶子节点存储了行记录,根据这个唯一的索引,MySQL 就能确定搜索的记录。

回表case 

使用 drinker_id 这个索引来查询 drinker_id = 3 的记录时就会涉及到回表。

SELECT * FROM t_back_to_table WHERE drinker_id = 3;

因为通过 drinker_id 这个普通索引查询方式,则需要先搜索 drinker_id 索引树(该索引树上记录着主键ID的值),然后得到主键 ID 的值为 3,再到 ID 索引树搜索一次。这个过程虽然用了索引,但实际上底层进行了两次索引查询,这个过程就称为回表。

回表小结

  • 对比发现,基于非主键索引的查询需要多扫描一棵索引树,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
  • 在应用中应该尽量使用主键查询,这里表中就四条数据,如果数据量大的话,就可以明显的看出使用主键查询效率更高。
  • 使用聚集索引(主键或第一个唯一索引)就不会回表,普通索引就会回表。

四、索引存储结构

InnoDB 引擎的聚集索引和普通索引都是B+Tree 存储结构只有叶子节点存储数据

  • 新的B+树结构没有在所有的节点里存储记录数据,而是只在最下层的叶子节点存储,上层的所有非叶子节点只存放索引信息,这样的结构可以让单个节点存放更多索引值,增大Degree 的值,提高命中目标记录的几率。
  • 这种结构会在上层非叶子节点存储一部分冗余数据,但是这样的缺点都是可以容忍的,因为冗余的都是索引数据,不会对内存造成大的负担。

聚簇索引

id 是主键,所以是聚簇索引,其叶子节点存储的是对应行记录的数据

聚簇索引存储结构

如果查询条件为主键(聚簇索引),则只需扫描一次B+树即可通过聚簇索引定位到要查找的行记录数据。

如:

SELECT * FROM t_back_to_table WHERE id = 1;

查找过程:

聚簇索引查找过程 

普通索引

drinker_id 是普通索引(二级索引),非聚簇索引叶子节点存储的是聚簇索引的值,即主键ID的值

普通索引存储结构

如果查询条件为普通索引(非聚簇索引),需要扫描两次B+树

  • 第一次扫描通过普通索引定位到聚簇索引的值
  • 第二次扫描通过第一次扫描获得的聚簇索引的值定位到要查找的行记录数据。

如:

SELECT * FROM t_back_to_table WHERE drinker_id = 1;

(1)第一步,先通过普通索引定位到主键值id=1

2第二步,回表查询,再通过定位到的主键值即聚集索引定位到行记录数据。

普通索引查找过程

五、如何防止回表

既然我们知道了有回表这么回事,肯定就要尽可能去防微杜渐。最常见的防止回表手段就是索引覆盖,通过索引打败索引。

索引覆盖

为什么可以使用索引打败索引防止回表呢?因为其只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表查询

例如:SELECT * FROM t_back_to_table WHERE drinker_id = 1;

如何实现覆盖索引?

常见的方法是将被查询的字段,建立到联合索引中。

解释性SQL的explain的输出结果Extra字段为Using index时表示触发了索引覆盖。

No覆盖索引case1

继续使用之前创建的 t_back_to_table 表,通过普通索引drinker_id 查询id 和 drinker_id 列。

EXPLAIN SELECT id, drinker_id FROM t_back_to_table WHERE drinker_id = 1;

explain分析:为什么没有创建覆盖索引Extra字段仍为Using index,因为drinker_id是普通索引,使用到了drinker_id索引,在上面有提到普通索引的叶子节点保存了聚簇索引的值所以通过一次扫描B+树即可查询到相应的结果,这样就实现了隐形的覆盖索引即没有人为的建立联合索引。(drinker_id索引上包含了主键索引的值

No覆盖索引case2

继续使用之前创建的 t_back_to_table 表,通过普通索引drinker_id查询 iddrinker_iddrinker_feature列数据。

EXPLAIN SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

explain分析:drinker_id是普通索引其叶子节点上仅包含主键索引的值,而 drinker_feature 不在索引树上,所以通过drinker_id 索引在查询到id和drinker_id的值后,需要根据主键id 进行回表查询,得到 drinker_feature 的值。此时的Extra列的NULL表示进行了回表查询。

覆盖索引case

为了实现索引覆盖,需要建组合索引 idx_drinker_id_drinker_feature(drinker_id,drinker_feature)

#删除索引 drinker_id

DROP INDEX drinker_id ON t_back_to_table;

#建立组合索引

CREATE INDEX idx_drinker_id_drinker_feature on t_back_to_table(`drinker_id`,`drinker_feature`);

继续使用之前创建的 t_back_to_table 表,通过覆盖索引 idx_drinker_id_drinker_feature 查询 iddrinker_iddrinker_feature列数据。

EXPLAIN SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

explain分析:此时字段drinker_iddrinker_feature是组合索引idx_drinker_id_drinker_feature,查询的字段id、drinker_iddrinker_feature的值刚刚都在索引树上,只需扫描一次组合索引B+树即可,这就是实现了索引覆盖,此时的Extra字段为Using index表示使用了索引覆盖。

六、索引覆盖优化SQL场景

适合使用索引覆盖来优化SQL的场景如全表count查询、列查询回表和分页查询等。

全表count查询优化

#首先删除 t_back_to_table 表中的组合索引

DROP INDEX idx_drinker_id_drinker_feature ON t_back_to_table;

EXPLAIN SELECT COUNT(drinker_id) FROM t_back_to_table

explain分析:此时的Extra字段为Null 表示没有使用索引覆盖。

使用索引覆盖优化,创建drinker_id字段索引。

#创建 drinker_id 字段索引

CREATE INDEX idx_drinker_id on t_back_to_table(drinker_id);

EXPLAIN SELECT COUNT(drinker_id) FROM t_back_to_table

explain分析:此时的Extra字段为Using index表示使用了索引覆盖。

列查询回表优化

前文在描述索引覆盖使用的例子就是列查询回表优化。

例如:

SELECT id, drinker_id, drinker_feature FROM t_back_to_table WHERE drinker_id = 1;

使用索引覆盖:建组合索引 idx_drinker_id_drinker_feature on t_back_to_table(`drinker_id`,`drinker_feature`)即可

分页查询优化

#首先删除 t_back_to_table 表中的索引 idx_drinker_id

DROP INDEX idx_drinker_id ON t_back_to_table;

EXPLAIN SELECT id, drinker_id, drinker_name, drinker_feature FROM t_back_to_table ORDER BY drinker_id limit 200, 10;

explain分析:因为 drinker_id 字段不是索引,所以在分页查询需要进行回表查询,此时Extra为U sing filesort 文件排序,查询性能低下。

使用索引覆盖:建组合索引 idx_drinker_id_drinker_name_drinker_feature

#建立组合索引 idx_drinker_id_drinker_name_drinker_feature (`drinker_id`,`drinker_name`,`drinker_feature`)

CREATE INDEX idx_drinker_id_drinker_name_drinker_feature on t_back_to_table(`drinker_id`,`drinker_name`,`drinker_feature`);

再次根据 drinker_id 分页查询:

EXPLAIN SELECT id, drinker_id, drinker_name, drinker_feature FROM t_back_to_table ORDER BY drinker_id limit 200, 10;

explain分析:此时的Extra字段为Using index表示使用了索引覆盖。

 

 

 

 

五花马     千金裘             呼儿将出换美酒 与尔同销万古愁  

 

 

 

 

原文地址:https://www.cnblogs.com/taojietaoge/archive/2022/04/23/16167188.html

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

MySQL 回表

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

MySQL 回表

MySQL 回表     五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。 一、简述回表,顾名思义就是回到表中,也就是先通过普通索引扫描出数据所在的行,再通过行主键ID 取出索引中未包含的数据。所以回表的产生也是需要一定条件的,如果一次索引查询就能获得所有的s
MySQL 回表
2017-09-24

mysql怎么避免回表

要避免回表的情况,可以考虑以下几个方面:使用合适的索引:在关键列上创建索引,这样查询可以直接从索引中获取数据,而不需要回表查询。使用覆盖索引:在查询中只使用索引列,而不使用其他列,这样可以避免回表查询。使用联合索引:将多个列组合成一个索引,
mysql怎么避免回表
2023-10-28

mysql回表查询是什么,回表查询的使用

目录javascript聚集索引和非聚集索引聚集索引和非聚集索引的区别那回表是什么验证在说到什么是回表查询的时候,有两个概念需要先解释清楚:分别是聚集索引(聚簇索引)和非聚集索引(非聚簇索引)聚集索引和非聚集索引mysql规定,在使用I
2022-11-21

mysql 14 覆盖索引+回表

覆盖索引概念:    MySQL可以利用索引返回select列表中的字段值(就是索引值)。而不必根据主键再次读取聚簇索引数据文件查到数据,也就是平时所说的不需要回表操作。覆盖索引其实是索引覆盖的意思,索引字段就已经囊括select查询的字段,即索引字段覆盖了需
mysql 14 覆盖索引+回表
2020-11-10

MySQL回表的性能消耗是多少

这篇文章主要介绍“MySQL回表的性能消耗是多少”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“MySQL回表的性能消耗是多少”文章能帮助大家解决问题。1 回表的性能消耗无论单列索引 还是 联合索引,
2023-06-29

一文解答什么是MySQL的回表

这篇文章主要介绍了一文解答什么是MySQL的回表,回表就是 MySQL要先查询到主键索引,然后再用主键索引定位到数据,文章围绕主题展开详细的内容介绍,需要的朋友可以参考一下
2022-11-13

MySQL 回表,覆盖索引,索引下推

目录回表覆盖索引索引下推无索引下推: 查看索引下推的状态有索引下推: 开启索引下推回表在研究mysql二级索引的时候,发现Mysql回表这个操作,往下研究了一下字面意思,找到索引,回到表中找数据解释一下就是:先通过索引扫描出数据所在
2022-07-11

当MySQL算术表达式返回NULL时?

我们知道 NULL 不是一个值,它也不同于零。如果我们在其中使用 NULL,MySQL 算术表达式将返回 NULL。可以通过以下示例来理解 -示例mysql> Select 100*NULL;+----------+| 100*NULL
2023-10-22

MySQL快速回顾:数据库和表操作

前提要述:参考书籍《MySQL必知必会》利用空闲时间快速回顾一些数据库基础。4.1 连接在最初安装MySQL,可能会要求你输入一个管理登录(通常为root)和一个口令(密码)。连接MySQL需要以下信息:主机名(计算机名)——如果连接到本地MySQL服务器,为
MySQL快速回顾:数据库和表操作
2021-02-09

MySQL数据库表空间回收的解决

目录1. mysql表空间回收2. MySQL表空间设置3. MySQL删除数据流程4. MySQL数据页空洞问题1. MySQL表空间回收我们经常会发现一个问题,就是把表数据删除以后发现,数据文件大小并没有变化,这就是标题中所说的MyS
2023-02-03

【MySQL系列】-回表、覆盖索引真的懂吗

【MySQL系列】-回表、覆盖索引真的懂吗 文章目录 【MySQL系列】-回表、覆盖索引真的懂吗一、MYSQL索引结构1.1 索引的概念1.2 索引的特点1.3 索引的优点1.4 索引的缺点 二、B-Tree与B+Tree2.
2023-08-21

编程热搜

目录