【后端】mysql 核心概念:回表,索引覆盖

55 阅读2分钟

本人是一名前端,最近因工作需要需要补充后端数据库相关的知识。索引这块是数据库中的重点难点,因此有了这篇文章。

我们用一个例子来理解这几个概念。

首先,创建表:

CREATE TABLE IF NOT EXISTS test(
id int(11) NOT NULL,
age int(11) NOT NULL,
name varchar(255) NOT NULL DEFAULT '',
school varchar(255),
PRIMARY KEY(id),
KEY idx_age_name (age, name) USING BTREE
)

有两个索引,一个是主键索引,一个是普通联合索引。所谓主键索引,就是聚簇索引,表的叶子节点存放行数据,每个表有且只有一个主键索引。对于,innodb 而言,普通索引的叶子结点存的是主键值。

接下来,我们给表添加数据:

INSERT INTO test(id,age,name,school) values(1,10,'小明','hust'),(2,11,'小红','hust'),(3,12,'小伟','cu');

接着,我们用 DESC 或 EXPLAIN 查看此语句是否进行了回表:

DESC SELECT age,name,school FROM test WHERE age = 11;

image.png

Extra 字段是 null,证明回表了,效率下降,这是因为首先要根据普通索引 idx_age_name 查到主键 id。然后再通过主键索引进一步定位所有的数据。这一过程叫回表。

为了避免回表的性能损耗,mysql 有一种机制,如果它可以在索引(往往是联合索引)中发现所有需要查询的字段,那么它就不需要进行回表查询。

我们画一下普通索引 idx_age_name 长什么样(加入一页可以存放两个索引项,一页只能存放一个叶子结点):

image.png

在这个图里面,school 的信息并不存放在叶子节点,只能拿到 id 用主键聚簇索引再去查一遍,导致回表。但是下面这一条查询则不需要:

DESC SELECT age,name FROM test WHERE age = 11;

因为 age 和 name 在索引的叶子节点就已经包含了,因此不需要回表,这种现象被称为索引覆盖,即,索引中已经覆盖了所有需要查找的字段。

通过本例,回表,索引覆盖应该是讲清楚了。