关于回表的一些讨论

本文探讨了数据库中的回表查询现象,解释了回表的定义,即通过非主键索引查找数据时需要返回主键索引获取完整信息。同时,介绍了聚簇索引和非聚簇索引的区别,聚簇索引将数据与索引存储在一起,而非聚簇索引的叶子节点存储数据行位置。此外,文章还阐述了覆盖索引的概念,它是查询仅通过索引即可完成,避免了回表操作。通过覆盖索引可以提高查询效率,当查询使用到的列完全被索引覆盖时,EXPLAIN的Extra列会显示'Using index'。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一觉醒来,技术交流群的大佬们又在讨论技术了,今天讨论的是回表的问题,在这里记录一下,方便日后的学习和复习
在这里插入图片描述
首先,什么是回表查询?
回表就是先通过数据库索引扫描出数据所在的行,再通过行主键id取出索引中未提供的数据,即基于非主键索引的查询需要多扫描一棵索引树。从普通索引B+树搜索回到主键索引B+树搜索的过程称为回表

众所周知,主键索引是聚簇索引,而一张表里面只能有一个聚簇索引,所以非主键索引也就是非聚簇索引

顺便聊一下聚簇索引和非聚簇索引的区别吧
聚簇索引:将数据存储和索引放在一起,并且是按照一定的顺序的,找到了索引也就意味着找到了数据,数据的物理存放顺序和索引是一致的,换句话说,只要索引是相邻的,那么对应的数据在磁盘上也一定是相邻的
非聚簇索引:叶子节点不存储数据而是存储数据行的地址,如果想要在磁盘查找数据,需要根据索引查找到数据行的位置再去磁盘查找数据

都知道可以通过覆盖索引来避免回表,那么覆盖索引是什么呢?
有下面三种理解:

解释一: 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
解释二: 索引是高效找到行的一个方法,当能通过检索索引就可以读取想要的数据,那就不需要再到数据表中读取行了。如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫 做覆盖索引。
解释三:是非聚集组合索引的一种形式,它包括在查询里的Select、Join和Where子句用到的所有列(即建立索引的字段正好是覆盖查询语句[select子句]与查询条件[Where子句]中所涉及的字段,也即,索引包含了查询正在查找的所有数据)。
  
当发起一个被索引覆盖的查询(也叫作索引覆盖查询)时,在EXPLAIN的Extra列可以看到“Using index”的信息

在这里插入图片描述

从执行结果上看,这个SQL语句只通过索引,就取到了所需要的数据,这个过程就叫做索引覆盖。

<think>我们正在讨论的是数据库中的回表询概念及其工作原理。根据用户提供的引用内容,我们可以结合索引的相关知识来解释回表询。引用[1]和[3]提到了索引的概念和作用:索引是加快检索表中数据的方法,通过形结构(如B+)直接定位记录,避免全表扫描。回表询是数据库询过程中的一种现象,通常发生在使用非聚簇索引(二级索引)的情况下。为了理解回表询,我们需要先了解聚簇索引和非聚簇索引。1.聚簇索引(ClusteredIndex):将数据存储与索引放到一块,索引结构的叶子节点保存了行数据。一张表只能有一个聚簇索引,通常是主键索引。2.非聚簇索引(SecondaryIndex,也称二级索引):叶子节点存储的是主键值,而不是行数据。当通过非聚簇索引询时,如果询的字段不在索引中(即非覆盖索引的情况),就需要根据索引中存储的主键值回到聚簇索引中找对应的行数据,这个过程称为回表回表询的工作原理:步骤1:在非聚簇索引找,找到对应的主键值。步骤2:根据主键值,回到聚簇索引找完整的行数据。例如,有一张员工表(emp),其中id是主键(聚簇索引),我们在name字段上建立了非聚簇索引。执行如下询:SELECT*FROMempWHEREname='张三';如果使用name索引,那么:-首先通过name索引找到'张三'对应的主键id(比如id=1)。-然后通过主键id在聚簇索引找,得到该id对应的完整行数据。这里,第二步的询就是回表操作。由于需要两次索引找,所以回表询通常比直接使用聚簇索引询或覆盖索引询要慢。如何避免回表询?使用覆盖索引(CoveringIndex):在非聚簇索引中包含了询所需要的所有字段,这样就不需要回表了。例如,如果只需要询id和name,那么我们可以创建索引(name,id),这样在name索引中就可以直接获取到id,不需要回表。引用[1]中提到索引可以提高询性能,但回表询会带来额外的开销,因此需要合理设计索引以避免不必要的回表。根据引用[3]中提到的聚簇索引的特性(一张表只能有一个聚簇索引),我们可以理解为什么非聚簇索引需要回表。另外,引用[2]中提到了连接和子询的优化,虽然与回表无直接关系,但提醒我们在设计询时要注意性能。下面我们用图示来展示回表询的过程:聚簇索引(主键索引):(根节点)/\(中间节点)(中间节点)/\/\(叶子节点:主键id1,行数据1)(叶子节点:主键id2,行数据2)...非聚簇索引(name索引):(根节点)/\(中间节点)(中间节点)/\/\(叶子节点:name='张三',主键id1)(叶子节点:name='李四',主键id2)...回表过程:通过name='张三'找到主键id1,然后通过主键id1在聚簇索引中找到行数据1。因此,回表询会增加磁盘I/O次数(两次索引找),尤其是在数据量大的情况下,可能成为性能瓶颈。总结:回表询是当使用非聚簇索引时,为了获取完整数据而回到聚簇索引中找的过程。优化回表询的方法主要是使用覆盖索引,将询需要的字段包含在非聚簇索引中。现在,我们可以生成相关问题:1.什么是覆盖索引?如何创建覆盖索引来避免回表?2.聚簇索引和非聚簇索引在存储结构上有什么区别?3.回表询在哪些情况下会严重影响数据库性能?如何优化?4.在InnoDB存储引擎中,主键索引一定是聚簇索引吗?注意:引用标记需要自然添加,我们在回答中引用了引用[1]和引用[3]的内容,所以末尾要加上引用标记。</think>### 数据库中的回表询详解 #### 一、回表询概念 **回表询**是数据库询过程中的一个关键环节,指当使用**非聚簇索引**(二级索引)进行询时,数据库需要**再次访问聚簇索引**才能获取完整数据行的过程。其本质是索引数据与行数据分离导致的额外I/O操作[^1][^3]。 #### 二、工作原理(以B+索引为例) ```mermaid graph LR A[询请求] --> B{使用二级索引} B --> C[在二级索引B+中定位] C --> D[获取主键ID值] D --> E{询列是否在索引中?} E -->|是| F[直接返回结果<br>(覆盖索引)] E -->|否| G[通过主键ID回表] G --> H[在聚簇索引B+中定位行] H --> I[获取完整数据行] ``` 1. **二级索引询阶段** - 数据库扫描二级索引B+(如`name_index`) - 定位到叶子节点获取**主键ID值**(如`id=5`) - *此时仅获得主键值,不包含其他列数据* 2. **回表阶段** - 根据主键ID值`id=5`回到**聚簇索引**(主键索引) - 在聚簇索引B+中定位对应行 - 读取磁盘上的**完整数据行** > **示例场景**: > 表结构:`users(id PK, name, age, address)` > 索引:`INDEX name_index(name)` > 询:`SELECT age FROM users WHERE name = '张三'` > 过程: > 1. 通过`name_index`找到`name='张三'`对应的`id=5` > 2. 通过`id=5`回聚簇索引获取`age`值 #### 三、性能影响与优化 | 场景 | I/O次数 | 性能 | 优化方案 | |------|---------|------|----------| | **覆盖索引** | 1次 | ⭐⭐⭐⭐⭐ | 创建包含所有询列的索引<br>`INDEX name_age(name, age)` | | **回表询** | 2+次 | ⭐⭐ | 避免`SELECT *`,只取必要字段 | | **全表扫描** | N次 | ⭐ | 对WHERE条件列建索引 | **优化建议**: 1. **使用覆盖索引**:确保索引包含询所需的所有列[^1] ```sql -- 创建覆盖索引避免回表 CREATE INDEX idx_name_age ON users(name, age); ``` 2. **减少询字段**:避免`SELECT *`,只选择必要字段 3. **聚簇索引设计**:将频繁访问的列放入聚簇索引(如InnoDB主键) 4. **索引合并**:对多条件询使用复合索引[^2] ```sql -- 复合索引可避免多次回表 CREATE INDEX idx_name_address ON users(name, address); ``` #### 四、与索引类型的关系 1. **聚簇索引**(如InnoDB主键) - 叶子节点存储完整数据行 - 无需回表 - *每表仅允许一个聚簇索引*[^3] 2. **非聚簇索引**(二级索引) - 叶子节点存储主键值 - 询非索引列时必须回表 - *每表可创建多个* > **特殊场景**:当二级索引包含所有询列时(覆盖索引),即使是非聚簇索引也不会触发回表[^1]。 --- ### 相关问题 1. 如何通过执行计划识别SQL询是否发生了回表操作? 2. 在哪些场景下即使存在索引也无法避免回表询? 3. 聚簇索引和非聚簇索引在物理存储结构上有何本质区别? 4. 如何平衡索引数量和回表询性能之间的关系?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值