MySQL 索引基础

MySQL 默认存储引擎 InnoDB ,下文若无特殊说明均指 InnoDB。索引本质:排好序的数据结构,用来加速查询,代价是占用磁盘、减慢增删改

一、索引是什么

索引是存储引擎用于快速定位行数据的数据结构

没有索引:全表扫描(逐行对比)

有索引:通过索引结构快速找到目标数据地址,再读取数据。

类比:一本书的目录,通过目录快速找到页码,不用逐页翻阅。

二、InnoDB 索引底层结构:B+Tree

1. 为什么不用二叉树、B 树、Hash?

  1. 二叉搜索树:极端退化成链表,高度很高,磁盘 IO 多;

  2. 平衡二叉树(AVL / 红黑树):树高依然大,IO 次数多;

  3. B-Tree:数据和索引键全都存在所有节点,页存储利用率低;

  4. Hash 索引 :等值查询快,不支持范围查询、排序、前缀模糊查询,InnoDB 不支持手动 Hash 索引;

  5. B+Tree(InnoDB 选择)

    • 所有数据只存在叶子节点
    • 非叶子节点只存索引键,内存可缓存更多索引,树更低;
    • 叶子节点用双向链表相连,天然支持范围查询、order by、分页;
    • 叶子节点有序,范围遍历非常高效。

B+Tree 结构要点

  • 一页大小默认 16KB(InnoDB 页);
  • 一页存放很多索引条目,树高度通常 3~4 层;
  • 一次查询最多 3~4 次磁盘 IO。

三、InnoDB 两大索引类型:聚簇索引 & 二级索引(重点!)

1. 聚簇索引(主键索引)

InnoDB 必须有聚簇索引,一张表只能一个聚簇索引

规则:

  1. 如果显式定义 PRIMARY KEY → 主键作为聚簇索引;
  2. 没有主键,选第一个唯一非空索引作为聚簇索引;
  3. 都没有,InnoDB 自动生成隐藏 6 字节 rowid 作为聚簇索引。

特点:

  • B+Tree 叶子节点 存放完整的一行数据

  • 索引顺序就是数据物理存储顺序;

  • 查询主键:直接拿到整行数据 → 主键查找最快

    CREATE TABLE user(
    id INT PRIMARY KEY, -- id 就是聚簇索引
    name VARCHAR(20),
    age INT
    );

2. 二级索引(普通索引、辅助索引)

除聚簇索引以外所有索引都是二级索引。

关键特性:

✅ 二级索引叶子节点 不存完整数据,只存:索引键 + 主键值

重点记忆:通过二级索引查到主键,再拿主键去聚簇索引查找完整数据 → 回表查询

查询流程示例:

SELECT * FROM user WHERE name = '张三'

  1. 在 name 二级索引找到 name="张三",拿到主键 id;
  2. 使用 id 去聚簇索引 B + 树查找整行数据;
  3. 这一步就是回表,产生两次 B + 树查找。

覆盖索引(优化核心)

如果查询字段全部在二级索引里,不需要回表,称为覆盖索引。

复制代码
-- name建有索引,查询只查name、id(id自带在二级索引叶子)
SELECT id,name FROM user WHERE name='张三';
-- ✅ 覆盖索引,无需回表,性能极高

执行计划里 Extra 显示 Using index 代表命中覆盖索引。

四、索引分类(语法层面)

1. 主键索引 PRIMARY KEY

  • 唯一、非空;一张表仅一个;就是聚簇索引。

2. 唯一索引 UNIQUE

索引列值不能重复,允许一个 NULL;属于二级索引。

复制代码
UNIQUE INDEX uk_phone(phone)

3. 普通索引 INDEX

最基础索引,允许重复、允许 NULL。

复制代码
INDEX idx_name(name)

4. 联合索引(复合索引)

多个字段合在一起建立一个索引 INDEX idx_a_b_c(a,b,c)

👉 最左匹配原则(重中之重)

索引有序:先排序 a,a 相同排序 b,b 相同排序 c。

执行的查询语句是必须要有a

生效情况:

✅ where a=?

✅ where a=? and b=?

✅ where a=? and b=? and c=?

✅ where a=? and b>? and c=? (范围条件后面索引失效)

❌ where b=?

❌ where b=? and c=?

❌ where c=?

先按 a 排序;只有在同一个a的时候,不同b才是有序的。那么对于范围查询会出现多组不同的a,那么后面的b不一定有序,于是后面索引失效
口诀:遇到范围(> < between like 前缀 %),后面索引失效

5. 前缀索引

长字符串(varchar、text等),只取前面若干字符建立索引,节省空间

复制代码
create index idx_email on table_name(email(10)); -- 只使用email字段的前10个字符建立索引

缺点:无法使用覆盖索引,不能用于 order by/group by

前缀索引的选择性

选择性 = 不重复值数量 / 总行数

值域范围:(0,1]

  • 越接近 1:选择性越好,索引高效
  • 越接近 0:大量重复,索引基本没用

前缀索引风险:截取前缀后,选择性下降

如果前 N 位大量重复,索引失效、回表暴增。

6. 全文索引 FULLTEXT

针对大文本(varchar/text)分词检索,替代模糊 %xxx%,不适合超大并发场景,一般业务优先 Elasticsearch。

五、索引失效常见场景

  1. 联合索引不满足最左前缀

  2. 索引列参与运算、函数调用

    WHERE YEAR(create_time) = 2026; -- 失效
    WHERE create_time >= '2026-01-01'; -- 正确写法

  3. 隐式类型转换

    -- phone字段用的是字符串类型的索引,但条件传入数字,自动发生隐式类型转换,导致索引失效
    WHERE phone = 13800138000;

  4. 模糊匹配
    like '%关键词' 通配符在前不能走索引;like '关键词%' 可以走索引

  5. 使用 or,一侧没有索引(可改用 union all 优化)

  6. not in / != / is not null 大多情况无法有效利用索引

  7. MySQL 优化器判断全表扫描更快,主动放弃索引(数据量占表 30% 左右常出现)

六、索引优缺点

✅ 优点

  1. 大幅加快 WHERE 条件查询
  2. 加速 ORDER BY / GROUP BY / DISTINCT 排序分组
  3. 唯一索引保证数据唯一性

❌ 缺点

  1. 占用磁盘空间
  2. DML 操作(insert/update/delete)需要维护 B + 树,降低写入性能
  3. 过多索引导致维护成本极高

七、建立索引最佳实践

  1. 优先给 WHERE、JOIN、ORDER BY、GROUP BY 的字段建索引;
  2. 联合索引把区分度高、等值查询放前面,范围条件放最后;
  3. 尽量使用覆盖索引,避免回表;
  4. 字符串优先前缀索引,不要对超长字段建完整索引;
  5. 不建无用索引,定期清理长期不用的索引;
  6. 主键尽量使用自增 INT/BIGINT(有序插入,避免 B + 树频繁分裂;UUID 主键随机插入会大量页分裂,性能差)。

八、补充:MyISAM 索引简单区别

MyISAM没有聚簇索引,全部是二级索引;

索引文件 (.MYI) 和数据文件 (.MYD) 分离;

叶子节点存储数据行的物理文件偏移地址

不支持事务、崩溃不安全,业务开发基本不用。

相关推荐
码事漫谈39 分钟前
我啥都能做,却不知道做什么了
后端
geovindu1 小时前
CSharp: 万年历
开发语言·后端·c#·.net
2601_962177132 小时前
【MySQL篇】聚合查询,联合查询
android·java·mysql
云贝教育-郑老师2 小时前
MySQL 的“黑匣子“:Performance Schema 把数据库内部变成一张可查的表
数据库·mysql
独泪了无痕2 小时前
SpringBoot Event事件机制,轻松实现业务解耦
spring boot·后端·spring
悲且狂3 小时前
SpringBoot项目改造注意事项(旧项目框架复用)
java·spring boot·后端
敲代码的嘎仔4 小时前
28届后端开发-海康威视日常实习一面(已OC)
java·开发语言·后端·面试·海康威视·实习·大厂
青春之我_XP4 小时前
MySQL 常用日期函数 实战指南
数据库·sql·mysql·数据分析·数据库开发·日期函数
IT_陈寒4 小时前
JavaScript数组排序踩的坑,差点让我加班到凌晨
前端·人工智能·后端