mysql索引

一、索引的介绍

索引本质上就是为"主数据"(表数据)创建的额外"导向数据"。它的核心目的是通过牺牲一定的磁盘空间(存储这些额外的导向结构),来换取极大的查询速度提升(用空间换时间)。

二、 什么是聚簇索引?什么是二级索引?

在 InnoDB 存储引擎中,索引主要分为两种:

  • 聚簇索引 (Clustered Index):

    • 定义: 决定了表中数据的物理存储顺序 。在 InnoDB 中,数据行本身就是存储在聚簇索引的叶子节点上的。

    • 特点: 每个 InnoDB 表有且仅有一个聚簇索引。如果表定义了主键,主键就是聚簇索引;如果没有主键,会选择一个非空且唯一的列作为聚簇索引;如果都没有,InnoDB 会隐式创建一个 6 字节的隐藏列作为聚簇索引。

  • 二级索引 (Secondary Index / 非聚簇索引):

    • 定义: 除了聚簇索引之外的所有其他索引,都属于二级索引(如普通索引、唯一索引、联合索引等)。

    • 特点: 一个表可以建立多个二级索引。

也就是说聚簇索引,在没有二级索引的情况下: 一般有主键表,聚簇索引就是他的主键(没有的情况也无妨,会有隐藏列作为聚簇索引) 然后对于他的b+树,非叶子节点和叶子节点就是存储主键值(因为整棵树就是根据它构建的嘛) 只是特别的,非叶子节点还会存储指向子节点的指针,而叶子节点则只存储完整的用户行数据。

对于二级索引,一般是搭配着聚簇节点来用, 对于其B+树,同样的,非叶子节点和叶子节点都是存储索引列的值(因为整棵树就是根据它构建的嘛) 特别的,非叶子节点会存储指向子节点的指针,而叶子节点,会存储对应行的主键值

三、什么是回表, 二级索引的回表是在聚簇索引上查找吗?

回表(Table Lookup) ,在数据库(特别是 InnoDB 引擎)中,指的是先通过二级索引找到主键,再拿着这个主键去聚簇索引中查找完整行数据的过程。

数据库中的具体执行流程

假设有一张 students 表,建表语句如下:

sql 复制代码
CREATE TABLE students (
    id INT PRIMARY KEY,          -- 主键
    name VARCHAR(50),            -- 姓名
    age INT                      -- 年龄
);

并且我们给 name 字段加了一个二级索引:CREATE INDEX idx_name ON students(name);。

当你执行查询语句:SELECT * FROM students WHERE name = '张三'; 时,数据库会发生以下动作:

  1. 第一次查找(走二级索引 idx_name) : 数据库在 idx_name 的 B+ 树中查找,发现 '张三' 对应的主键 id = 1001 。 注意:因为 idx_name 的叶子节点只存了 (name='张三', id=1001),并没有 age 等其他字段的信息。

  2. 第二次查找(回表,走聚簇索引) : 数据库拿着刚刚找到的主键 1001 ,转身去 students 表的聚簇索引(主键索引)B+ 树上查找。

  3. 最终拿到数据 : 在聚簇索引的叶子节点上,找到了 id = 1001 的完整一行数据(包含 id, name, age),然后将结果返回给你。

这**"根据二级索引找到主键,再用主键去聚簇索引查整行数据"**的两步操作,就是回表。

四、为什么要尽量避免回表?

回表会带来额外的磁盘 IO(输入输出)和 CPU 开销。因为你需要去两棵不同的 B+ 树上各查找一次。

  • 在普通的小数据量查询中,回表无伤大雅。

  • 但在高并发、大数据量的系统中,频繁的回表会严重拖慢数据库的响应速度。

因此,我们在实际开发中,会通过覆盖索引等手段来极力避免回表。

五、覆盖索引

覆盖索引 并不是一种特殊的索引结构(它还是 B+ 树),而是一种查询优化的状态。

当你的查询语句所需要的所有字段 ,都已经包含在某个二级索引的叶子节点中时,数据库就不需要回表去聚簇索引查数据了,直接返回二级索引里的数据即可。这就叫"覆盖了查询需求"。

回顾刚才的 students 表:id (主键), name (二级索引), age。 二级索引 idx_name 的叶子节点里存的是:(name, id)。

❌ 需要回表的情况

sql 复制代码
SELECT * FROM students WHERE name = '张三';
-- 或者
SELECT id, name, age FROM students WHERE name = '张三';
  • 原因: 你需要 age,但 idx_name 里只有 (name, id),没有 age。数据库被迫拿着 id 回去找聚簇索引。

✅ 触发覆盖索引的情况

复制代码
SELECT id, name FROM students WHERE name = '张三';
  • 原因: 你只需要 id 和 name。巧了!二级索引 idx_name 的叶子节点里正好存着 (name, id)。

  • 结果: 数据库在二级索引树上找到数据后,直接就把结果给你了,省去了回表的那一次磁盘 IO。这就是覆盖索引。

六、怎么判断是否使用了覆盖索引?

在使用 EXPLAIN 分析 SQL 语句时,如果 Extra 列出现了以下字样,就说明命中了覆盖索引:

Using index

(注意:如果没有 Using index,而是 Using where,通常意味着发生了回表)

七、 联合索引

联合索引就是把多个字段打包成一棵 B+ 树,按字段顺序逐级排序;查询时必须从左到右连续匹配(最左前缀原则),遇到范围查询则后续字段失效;它最大的实战价值是更容易触发覆盖索引,从而避免回表。

1、联合索引的 B+ 树怎么存?

假设建联合索引 idx_name_age(name, age),它的 B+ 树结构如下:

  • 非叶子节点 :存储 (name, age) 的组合键值 + 指向子节点的指针。

  • 叶子节点 :存储 (name, age) + 主键 id。

排序规则(核心) :先按 name 排序;name 相同时,再按 age 排序;如果 name 和 age 都相同,最后按主键 id 排序。

2、最左前缀原则(为什么必须从左往右?)

因为 B+ 树的排序是"先 name 后 age"的,只有 name 确定后,age 才有序。

查询条件 能否走索引 原因
WHERE name = '张三' ✅ 命中最左列
WHERE name = '张三' AND age = 20 ✅ 连续命中两列
WHERE age = 20 ❌ 跳过最左列 name,age 全局无序
WHERE name = '张三' AND age > 20 ⚠️ name 走索引,age 是范围查询,后续列失效

范围查询截断原则 :一旦遇到 >、<、BETWEEN 等范围查询,该列之后的索引列就无法再利用 B+ 树的有序性了。

3、联合索引字段顺序怎么排?

等值查询字段放前面,范围查询字段放最后。

sql 复制代码
-- 假设高频查询:WHERE user_id = ? AND status = ? AND create_time > ?
-- ✅ 正确:等值字段在前,范围字段压轴
CREATE INDEX idx_uid_status_time ON orders(user_id, status, create_time);
​
-- ❌ 错误:范围字段 create_time 放中间,status 会被截断失效
CREATE INDEX idx_uid_time_status ON orders(user_id, create_time, status);
相关推荐
周小董1 小时前
[1372]mac安装git
1024程序员节
LabVIEW开发1 小时前
ESO 极大望远镜主动光学:LabVIEW 实时模块 + 多核 COTS 替代专用硬件的路径
labview·1024程序员节·labview知识·labview功能·labview程序
夜不会漫长1 小时前
C++ 多态学习笔记
1024程序员节
爱读书的小胖1 小时前
LeetCode 45 跳跃游戏 II C 语言解题分析
1024程序员节
nington011 小时前
金融信创国产AD重构:2027倒计时下的技术选型与落地路径
其他·1024程序员节
LabVIEW开发1 小时前
ISRO 卫星应答机可搬移 ATE:模块化仪器 + LabVIEW 把测试时间砍掉一半
labview·1024程序员节·labview知识·labview功能·labview程序
Yang96111 小时前
自适应放电加实时纠偏,解析TY-60H在矿石能源的接续表现
1024程序员节
吹什么轩1 小时前
LangChain 全解:为什么大模型应用需要一层“框架“
1024程序员节
一个平凡而乐于分享的小比特1 小时前
CODESYS V3.5 SP10 移植至 T536 平台实战指南(二)
1024程序员节·codesys·t536