从零起步学习MySQL || 第七章:初识索引底层运用及性能优化(结合底层数据结构讲解)

一、什么是 MySQL 中的索引?

定义:

索引是数据库中一种用于 加快数据查询速度 的数据结构。

它就像一本书的"目录"一样,可以让数据库更快地找到你想要的数据,而不需要从头到尾扫描整张表。

简单比喻:

假设有一本 1000 页的书:

  • 没有索引时:你找"第 732 页的某个内容",只能一页页翻;

  • 有索引时:你看目录就能直接跳转到对应页码。

在 MySQL 中,索引是存储引擎层(如 InnoDB)维护的一种 数据结构 ,常用的数据结构是 B+ 树


二、为什么需要索引?

索引的主要作用:

  1. 加快数据查询速度

  2. 加快排序、分组等操作

  3. 加快表的连接(JOIN)

  4. 通过唯一索引保证数据唯一性

但要注意:

  • 索引会占用额外的磁盘空间

  • 写入/更新/删除数据时也会维护索引,稍微降低写入性能。

所以:索引是"以空间换时间"的机制。


三、MySQL索引的分类

索引可以按多种维度分类:

我们主要从以下四种维度讲解:

1️⃣ 按 数据结构 分类

2️⃣ 按 物理存储 分类

3️⃣ 按 字段特性 分类

4️⃣ 按 字段个数 分类


① 按数据结构分类

类型 底层结构 特点 举例
B+树索引(最常用) B+ Tree 有序、支持范围查询 普通索引、主键索引、唯一索引
Hash索引 哈希表 查找速度快,但不支持范围查询 MEMORY 引擎中可用
R-Tree索引 空间索引结构 用于地理空间数据(GIS) 在 MyISAM 中支持
Full-text全文索引 倒排索引 用于全文搜索 在 MyISAM、InnoDB 中都可用

举例:

复制代码
-- 创建B+树索引(默认)
CREATE INDEX idx_name ON user(name);

-- 创建全文索引
CREATE FULLTEXT INDEX idx_content ON article(content);

② 按物理存储分类

类型 是否和数据一起存储 特点 举例
聚簇索引(Clustered Index) 数据与索引存储在一起 主键索引,每个表只能有一个 InnoDB 主键
非聚簇索引(Non-Clustered Index) 索引与数据分开存储 存储的是数据地址或主键值 普通索引、唯一索引

举例:

复制代码
-- InnoDB 聚簇索引
CREATE TABLE student (
  id INT PRIMARY KEY,       -- 主键聚簇索引
  name VARCHAR(50),
  age INT,
  INDEX idx_name(name)      -- 非聚簇索引
);

解释:

  • id 是主键 → 聚簇索引;

  • idx_name 是普通索引 → 非聚簇索引。


③ 按字段特性分类

类型 特点 举例
主键索引(Primary Key) 唯一且不能为 NULL,每个表只能有一个 PRIMARY KEY(id)
唯一索引(Unique Index) 值唯一但可为 NULL UNIQUE INDEX idx_email(email)
普通索引(Normal Index) 无限制 INDEX idx_name(name)
全文索引(Full-text Index) 用于文本匹配 FULLTEXT(content)

举例:

复制代码
CREATE TABLE user (
  id INT PRIMARY KEY,                -- 主键索引
  email VARCHAR(100) UNIQUE,         -- 唯一索引
  name VARCHAR(50), 
  INDEX idx_name(name),              -- 普通索引
  FULLTEXT INDEX idx_bio(bio)        -- 全文索引
);

④ 按字段个数分类

类型 特点 举例
单列索引 只对一个字段建立索引 INDEX idx_name(name)
联合索引(复合索引) 对多个字段联合建立索引 INDEX idx_user(name, age)

举例:

复制代码
-- 单列索引
CREATE INDEX idx_name ON user(name);

-- 联合索引
CREATE INDEX idx_name_age ON user(name, age);

注意:

联合索引遵循 最左前缀原则 ,即索引 (name, age) 实际上相当于对:

  • (name)

  • (name, age)

    有索引效果,但对 (age) 单独无效。


四、总结表格一览

分类维度 类型 示例
按数据结构 B+树索引、Hash索引、R-Tree、全文索引 CREATE INDEX idx_name ON user(name)
按物理存储 聚簇索引、非聚簇索引 主键索引 vs 普通索引
按字段特性 主键、唯一、普通、全文 PRIMARY KEY(id)UNIQUE(email)
按字段个数 单列索引、联合索引 (name)(name, age)

补充讲解:

补充一 、最左匹配原则(Leftmost Prefix Rule)

✅ 1. 定义

"最左匹配原则"是指:

联合索引会从最左边的字段开始匹配,只有按照最左的字段顺序使用索引,索引才会生效。

换句话说:

MySQL 从联合索引的 第一个字段 开始匹配条件,

当中途遇到 范围查询(如 >, <, BETWEEN, LIKE 'xxx%' 时,就会停止继续匹配后面的字段。


✅ 2. 举例讲解

假设我们有如下表:

复制代码
CREATE TABLE user (
  id INT PRIMARY KEY,
  name VARCHAR(50),
  age INT,
  sex CHAR(1),
  INDEX idx_name_age_sex(name, age, sex)
);

索引的顺序是:(name, age, sex)


📘 例1:完整匹配(使用索引)

复制代码
SELECT * FROM user WHERE name = 'Tom' AND age = 20 AND sex = 'M';

✅ 按 (name, age, sex) 顺序依次匹配,用到了全部三列索引

👉 索引使用情况:name → age → sex


📘 例2:部分匹配(仍然使用索引)

复制代码
SELECT * FROM user WHERE name = 'Tom' AND age = 20;

✅ 用到了前两列 (name, age)

👉 索引使用情况:name → age


📘 例3:只用最左字段(仍使用索引)

复制代码
SELECT * FROM user WHERE name = 'Tom';

✅ 用到了索引的第一列 name

👉 索引使用情况:name


📘 例4:跳过最左字段(索引失效)

复制代码
SELECT * FROM user WHERE age = 20;

❌ 不符合最左匹配原则,因为没有使用 name

👉 索引 完全失效,需要全表扫描。


📘 例5:范围查询中断匹配

复制代码
SELECT * FROM user WHERE name = 'Tom' AND age > 18 AND sex = 'M';

✅ 只会使用 (name, age) 索引部分;

❌ 不会使用 sex,因为 age > 18 是范围查询。

👉 索引使用情况:name → age(停止)


📘 例6:顺序不一致但可优化

复制代码
SELECT * FROM user WHERE age = 20 AND name = 'Tom';

✅ 虽然条件顺序不一致,但 MySQL 优化器会自动调整 顺序,依然可以使用 (name, age) 索引。


📘 例7:LIKE 前缀匹配可用索引

复制代码
SELECT * FROM user WHERE name LIKE 'T%';

LIKE 'T%' 等价于范围查询 'T' <= name < 'U',仍然可用索引。

但:

复制代码
SELECT * FROM user WHERE name LIKE '%Tom%';

❌ 前面有通配符 %,索引失效。


三、索引下推(Index Condition Pushdown, ICP)

✅ 1. 定义

索引下推(ICP) 是 MySQL 5.6 引入的一种 索引优化机制

在没有 ICP 之前,MySQL 在通过索引定位到数据行后,会把这些行提取出来交给 Server 层 再做 WHERE 条件判断。

而有了 ICP 后,一部分 WHERE 条件判断可以 在存储引擎层完成,减少回表次数,提高性能。


✅ 2. 举例说明

仍然以这个表为例:

复制代码
CREATE TABLE user (
  id INT PRIMARY KEY,
  name VARCHAR(50),
  age INT,
  sex CHAR(1),
  INDEX idx_name_age(name, age)
);

📘 示例1:没有索引下推的情况
复制代码
SELECT * FROM user WHERE name LIKE 'Tom%' AND age = 20;

假设联合索引 (name, age)

  • name LIKE 'Tom%' 是范围查询;

  • 所以索引只能匹配 name,不能直接匹配 age

🔹 没有 ICP 时的执行逻辑:

  1. 通过索引扫描所有 name'Tom%' 开头的记录;

  2. 每匹配一条记录,都回表取完整行;

  3. 再在 Server 层 判断 age = 20

  4. 不符合的行被丢弃。

👉 缺点:回表次数太多(磁盘 IO 多)。


📘 示例2:启用索引下推的情况(ICP)

有 ICP 时:

  1. 通过索引扫描 name LIKE 'Tom%'

  2. 在索引层直接判断 age = 20

  3. 只有满足 age = 20 的行才回表取数据。

👉 优势:

  • 减少回表次数;

  • 大幅提升查询效率。


✅ 3. ICP 的触发条件

  • 必须是 InnoDB 或 MyISAM 引擎;

  • 必须是 联合索引

  • 查询语句中有 范围查询 + 其他条件

  • 其他条件可以在索引中判断的情况下才生效。

    最左匹配 + 索引下推 对比总结

    特性 最左匹配原则 索引下推(ICP)
    定义 联合索引从最左字段开始依次匹配,中断后不再匹配 在存储引擎层提前过滤数据,减少回表
    作用阶段 索引匹配阶段 存储引擎读取阶段
    是否减少IO
    是否加速查询
    触发条件 联合索引 范围查询 + 其他索引列条件
    执行计划标识 key=idx_xxx Using index condition
相关推荐
ScriptBIN5 小时前
管理和构建Java项目的工具--Maven
java·maven
全职计算机毕业设计5 小时前
基于SpringBoot框架的在线教育系统设计与实现(三套文档参考)
java·spring boot·后端
No8g攻城狮5 小时前
【异常解决】使用DateUtil.isSameDay()方法判断秒级时间戳是否属于同一天踩过的坑
java·jvm·spring boot·java-ee·springboot
IT_陈寒5 小时前
Python性能优化:5个被低估但效果惊人的内置函数实战解析
前端·人工智能·后端
再睡一夏就好5 小时前
【C++闯关笔记】深究继承
java·数据结构·c++·stl·学习笔记
6极地诈唬5 小时前
【sqlite】WAL初探
数据库·sqlite
PieroPc5 小时前
用Python Streamlit sqlite3 写一个简单博客
数据库·python·sqlite
啊森要自信5 小时前
【MySQL 数据库】使用C语言操作MySQL
linux·c语言·开发语言·数据库·mysql
千码君20165 小时前
Go语言:对其语法的一些见解
开发语言·后端·golang