面试常问 什么是回表?为什么需要回表?

news/2024/10/22 16:27:36/

小伙伴们在面试的时候,有一个特别常见的问题,那就是数据库的回表。什么是回表?为什么需要回表?

  1. 索引结构
    要搞明白这个问题,需要大家首先明白 MySQL 中索引存储的数据结构。这个其实很多小伙伴可能也都听说过,B+Tree 嘛!

B+Tree 是什么?那你得先明白什么是 B-Tree,来看如下一张图:
在这里插入图片描述

前面是 B-Tree,后面是 B+Tree,两者的区别在于:

B-Tree 中,所有节点都会带有指向具体记录的指针;B+Tree 中只有叶子结点会带有指向具体记录的指针。

B-Tree 中不同的叶子之间没有连在一起;B+Tree 中所有的叶子结点通过指针连接在一起。

B-Tree 中可能在非叶子结点就拿到了指向具体记录的指针,搜索效率不稳定;B+Tree 中,一定要到叶子结点中才可以获取到具体记录的指针,搜索效率稳定。

基于上面两点分析,我们可以得出如下结论:

B+Tree 中,由于非叶子结点不带有指向具体记录的指针,所以非叶子结点中可以存储更多的索引项,这样就可以有效降低树的高度,进而提高搜索的效率。

B+Tree 中,叶子结点通过指针连接在一起,这样如果有范围扫描的需求,那么实现起来将非常容易,而对于 B-Tree,范围扫描则需要不停的在叶子结点和非叶子结点之间移动。

对于第一点,一个 B+Tree 可以存多少条数据呢?以主键索引的 B+Tree 为例(二级索引存储数据量的计算原理类似,但是叶子节点和非叶子节点上存储的数据格式略有差异),我们可以简单算一下。

计算机在存储数据的时候,最小存储单元是扇区,一个扇区的大小是 512 字节,而文件系统(例如 XFS/EXT4)最小单元是块,一个块的大小是 4KB。InnoDB 引擎存储数据的时候,是以页为单位的,每个数据页的大小默认是 16KB,即四个块。

基于这样的知识储备,我们可以大致算一下一个 B+Tree 能存多少数据。

假设数据库中一条记录是 1KB,那么一个页就可以存 16 条数据(叶子结点);对于非叶子结点存储的则是主键值+指针,在 InnoDB 中,一个指针的大小是 6 个字节,假设我们的主键是 bigint ,那么主键占 8 个字节,当然还有其他一些头信息也会占用字节我们这里就不考虑了,我们大概算一下,小伙伴们心里有数即可:

16*1024/(8+6)=1170

即一个非叶子结点可以指向 1170 个页,那么一个三层的 B+Tree 可以存储的数据量为:

1170117016=21902400

可以存储 2100万 条数据。

在 InnoDB 存储引擎中,B+Tree 的高度一般为 2-4 层,这就可以满足千万级的数据的存储,查找数据的时候,一次页的查找代表一次 IO,那我们通过主键索引查询的时候,其实最多只需要 2-4 次 IO 操作就可以了。

大家先搞明白这个 B+Tree。

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

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

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

主键索引的叶子结点存储的是一行完整的数据。

非主键索引的叶子结点存储的则是主键值。

这就是两者最大的区别。

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

如果是通过主键索引来查询数据,例如 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,所以如果条件允许,还是建议在查询中优先选择通过主键索引进行搜索。

  1. 一定会回表吗?
    那么不用主键索引就一定需要回表吗?

不一定!

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

举个例子,我有如下一张表:
在这里插入图片描述

uname 和 address 字段组成了一个复合索引,那么此时,虽然这是一个二级索引,但是索引树的叶子节点中除了保存主键值,也保存了 address 的值。

我们来看如下分析:
在这里插入图片描述

可以看到,此时使用到了 uname 索引,但是最后的 Extra 的值为 Using index,这就表示用到了索引覆盖扫描(覆盖索引),此时直接从索引中过滤不需要的记录并返回命中的结果,这一步是在 MySQL 服务器层完成的,并且不需要回表。

  1. 扩展
    基于第一、二小节的分析,我们再来捋一捋为什么在数据库中建议使用自增主键。

自增主键往往占用空间比较小,int 占 4 个字节,bigint 占 8 个字节。由于二级索引的叶子节点存储的就是主键,所以如果主键占用空间小,意味着二级索引的叶子节点将来占用的空间小(间接降低 B+Tree 的高度,提高搜索效率)。

自增主键插入的时候比较快,直接插入即可,不会涉及到叶子节点分裂等问题(不需要挪动其他记录);而其他非自增主键插入的时候,可能要插入到两个已有的数据中间,就有可能导致叶子节点分裂等问题,插入效率低(要挪动其他记录)。

当然,这个是基于技术层面的讨论,如果业务上无法使用自增主键或者有其他要求导致无法使用自增主键,那没办法,在满足新要求的情况下重新选择一个最佳实践吧。


http://www.ppmy.cn/news/665242.html

相关文章

VMware虚拟机迁移到阿里云

1. 前言 最近公司内部研发部门有几台jenkins build机器运行在VMware平台上面,由于本地VMware平台底层计算资源不足导致虚拟机运行速度特别慢,每次版本发布都要build好久,而且VMware有时候计算资源不足,还会自动给占用资源大的机器…

c++ 引发异常:0xC0000005 :写入位置 0x00000189DD36A000时发生冲突

多次接收存到一个数组时,此时的接收数组要是全局的变量,其次只有接收的第一次是要new 这个数组要接收的大小,不然每次接收都会new一个这么大的数组

板岩Slate Digital发布FG-X 2 ML-1 FG-36A

近期板岩Slate Digital发布了三款新品插件,分别是FG-X2母带动态处理器、ML-1 87风格扩展包(用于VMS)、FG-36A老式复古激励器(用于VMR)。 Slate Digital由Steven Slate和Fabrice Gabriel于2008年共同创立,其…

一文详解Cookie,Session以及浏览器与Cookie,Session的底层联系

Cookie: 翻译过来就是饼干的意思Cookie是服务器用于保存键值对的一种技术,保存的是一段文本信息。客户端有了Cookie之后,每次请求都会发送给服务器每个Cookie的大小不能超过4KB 创建Cookie Cookie cookie new Cookie (“key”…

MongoDB 安装教程

基于作者Window10安装教程其他平台参考MongoDB官方手册 1.在这里我们直接下载这个64位版本 2.下载完成后,安装过程非常简单,选择自定义安装 3.这一步可以选择安装位置,你们可以自行选择 4.直接下一步next 5.最后install的时候 取消掉 i…

BL-HUF35A-AV-TRB 电子元器件 BRIGHT 封装SMD 批次2021

LM75BDP PBSS5240T PCA9548APW PCA9554APW PCA9539PW BL-HUF35A-AV-TRB BL-HUF33H-AV-TRE BL-HGE36A-AV-TRB

贴片DO-218AB封装TVS二极管SM8T33A

提及“汽车级瞬态二极管”,大多数电子工程师都会想到DO-218AB封装SM8S系列中的SM8S24V、SM8S33A、SM8S36A、SM8S36CA等等,这些物料常用于汽车12V系统和24系统中,可通过抛负载7637-5a/5b测试,为汽车电子产品安全保驾护航。关于汽车…

FRDM-KW36入门学习(二、运行heart_rate_sensor工程)

本次的参考文档是《Bluetooth Low Energy Quick Start Guide》 文档在协议栈文件夹下:C:\SDK_2.2.0_MKW36A512xxx4 (1)\docs\wireless\Bluetooth 1、打开工程,工程路径:C:\SDK_2.2.0_MKW36A512xxx4 (1)\boards\frdmkw36\wireless_examples\…