目录
前言:为什么你需要懂索引?
🔍 一、什么是索引?—— 从生活案例说起
🏠 生活中的“索引”例子
🧱 二、MySQL索引的底层结构:B+树详解
🌲 1. 为什么是B+树?不是哈希、不是二叉树?
📐 2. B+树长什么样?(图文解析)
🧩 三、MySQL中常见的索引类型
🔧 四、索引实战:如何创建与使用索引?
✅ 1. 创建索引的SQL语法
🔍 2. 如何查看索引是否生效?—— EXPLAIN 命令
⚠️ 五、索引设计的黄金法则与陷阱
✅ 正确使用索引的6大原则
🧪 六、实战案例:优化一个慢查询
🐌 场景:用户反馈“按姓名和年龄查人”太慢
🔍 分析步骤:
📈 七、索引的代价:不要盲目添加
🎯 总结:索引核心要点回顾
前言:为什么你需要懂索引?
你是否遇到过这样的场景?
- 执行一条SELECT * FROM users WHERE email = 'xxx@xx.com'花了5秒?
- 表中数据量从1万增长到100万后,原本很快的查询变得极其缓慢?
- DBA告诉你“加个索引就好了”,但你并不知道为什么?
索引就是MySQL查询性能的“加速器”。它就像一本书的目录,能让你快速定位到目标数据,而不需要一页一页翻找。
本篇文章将带你彻底搞懂MySQL索引的本质,让你不再只是“会写SQL”,而是真正理解“为什么这么写”。
🔍 一、什么是索引?—— 从生活案例说起
🏠 生活中的“索引”例子
想象一下你在图书馆找一本《深入理解MySQL》:
- 没有索引:你需要从第一排书架开始,一本一本翻,直到找到为止。这就是全表扫描。
- 有索引:图书馆有“书名索引表”,你先查索引表找到这本书在3楼A区5排,然后直接去拿。这就是索引查找。
👉 结论:索引牺牲了少量存储空间(索引表),换取了巨大的查询效率提升。
🧱 二、MySQL索引的底层结构:B+树详解
MySQL默认使用 InnoDB 存储引擎,其索引结构是 B+树(B-Plus Tree)。
🌲 1. 为什么是B+树?不是哈希、不是二叉树?
哈希表 | 精确查找极快 O(1) | 无法范围查询,不支持排序 | ❌ 仅适合Memory引擎 |
二叉搜索树 | 结构简单 | 容易退化为链表,性能不稳定 | ❌ |
B树 | 减少磁盘I/O | 非叶子节点也存数据,单次I/O利用率低 | ⭕ |
B+树 | 叶子节点连成链表,范围查询快,I/O效率高 | 略复杂 | ✅ InnoDB首选 |
📐 2. B+树长什么样?(图文解析)
[10|20]
/ | \\
[5,8] [15] [25,30]
/ | \\ / \\ / | \\
(5)(8)(10)(15)(20)(25)(30)(NULL)
B+树特点:
💡 小知识:InnoDB一页(Page)大小默认为 16KB,可存储上千个索引项。
🧩 三、MySQL中常见的索引类型
主键索引(PRIMARY KEY) | PRIMARY KEY(id) | 唯一 + 非空 + 聚簇索引 | 每张表必须有一个 |
唯一索引(UNIQUE) | UNIQUE KEY uk_email(email) | 值唯一,可为NULL | 防止重复(如邮箱) |
普通索引(INDEX) | INDEX idx_name(name) | 最基本的索引 | 提高查询速度 |
组合索引(Composite) | INDEX idx_name_age(name, age) | 多列组合,遵循最左前缀 | 多条件查询 |
全文索引(FULLTEXT) | FULLTEXT KEY ft_content(content) | 支持文本关键词搜索 | 文章、评论搜索 |
🔧 四、索引实战:如何创建与使用索引?
✅ 1. 创建索引的SQL语法
— 1. 创建表时添加索引
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
email VARCHAR(100),
age INT,
INDEX idx_name (name), — 普通索引
UNIQUE uk_email (email), — 唯一索引
INDEX idx_name_age (name, age) — 组合索引
);
— 2. 已有表添加索引
ALTER TABLE users ADD INDEX idx_age (age);
CREATE INDEX idx_email ON users(email);
— 3. 删除索引
DROP INDEX idx_age ON users;
🔍 2. 如何查看索引是否生效?—— EXPLAIN 命令
EXPLAIN SELECT * FROM users WHERE name = '张三';
关键字段解释:
type | 访问类型,const > ref > range > index > ALL(全表扫描) |
key | 实际使用的索引 |
rows | 扫描的行数(越少越好) |
Extra | 额外信息,如 Using index(覆盖索引)、Using where |
✅ 理想状态:type=ref 或 range,key 显示用了哪个索引,rows 很小。
⚠️ 五、索引设计的黄金法则与陷阱
✅ 正确使用索引的6大原则
最左前缀原则(组合索引核心!)
— 索引:(name, age, city)
SELECT * FROM users WHERE name='张三'; ✅ 用到索引
SELECT * FROM users WHERE name='张三' AND age=25; ✅ 用到索引
SELECT * FROM users WHERE age=25 AND city='北京'; ❌ 无法使用该组合索引(跳过name)
避免在索引列上使用函数或表达式
SELECT * FROM users WHERE YEAR(create_time) = 2025; ❌ 无法使用索引
— 应改为:
SELECT * FROM users WHERE create_time >= '2025-01-01' AND create_time < '2026-01-01'; ✅
尽量使用覆盖索引(避免回表)
— 索引:(name, age)
SELECT name, age FROM users WHERE name='张三'; ✅ 只查索引,无需回表(Extra: Using index)
SELECT * FROM users WHERE name='张三'; ❌ 需要回表查主键索引获取其他字段
选择区分度高的列建索引
- 区分度 = COUNT(DISTINCT col) / COUNT(*)
- 性别(男/女)区分度低,不适合单独建索引
- 邮箱、手机号区分度高,适合建索引
小表不一定需要索引
- 数据量 < 1000 行,全表扫描可能更快
避免过多索引
- 每个索引都会增加写操作(INSERT/UPDATE/DELETE)的开销
- 建议单表索引不超过6个
🧪 六、实战案例:优化一个慢查询
🐌 场景:用户反馈“按姓名和年龄查人”太慢
— 原始SQL(耗时2.3秒)
SELECT * FROM users WHERE name = '李四' AND age = 30;
🔍 分析步骤:
查看执行计划:
EXPLAIN SELECT * FROM users WHERE name = '李四' AND age = 30;
结果:type=ALL,key=NULL → 全表扫描!
解决方案:
— 添加组合索引
ALTER TABLE users ADD INDEX idx_name_age (name, age);
再次执行EXPLAIN:
- type=ref
- key=idx_name_age
- rows=3(假设匹配3条)
- 耗时:0.001秒 ✅
📈 七、索引的代价:不要盲目添加
✅ 大幅提升查询速度 | ❌ 占用磁盘空间 |
✅ 加速排序(ORDER BY) | ❌ 降低写入性能(INSERT/UPDATE/DELETE需维护索引) |
✅ 支持唯一性约束 | ❌ 过多索引影响维护成本 |
💡 建议:根据实际查询需求,有针对性地创建索引,定期审查无用索引并删除。
🎯 总结:索引核心要点回顾
底层结构 | B+树、聚簇索引、非叶子节点不存数据 |
类型选择 | 主键、唯一、普通、组合、全文 |
使用原则 | 最左前缀、避免函数、覆盖索引、高区分度 |
优化工具 | EXPLAIN、SHOW INDEX |
性能权衡 | 查询快 vs 写入慢、空间换时间 |
声明:本文基于MySQL 8.0 + InnoDB引擎,部分原理适用于其他版本。
评论前必须登录!
注册