Skip to content

MySQL 索引完全指南

索引优化是 SQL 性能调优的核心


1. 索引基础

1.1 什么是索引?

索引是一种特殊的数据结构,用来快速定位数据:

无索引:全表扫描 O(n)
有索引:B+树查找 O(log n)

1.2 索引类型

索引类型说明使用场景
主键索引唯一且非空PRIMARY KEY
唯一索引唯一但可空UNIQUE
普通索引普通列索引加速查询
全文索引文本搜索MATCH AGAINST
组合索引多列组合复合查询

2. B+ 树索引原理

2.1 数据结构

B+ 树特点:
- 所有数据都在叶子节点
- 叶子节点之间用链表连接
- 适合范围查询

2.2 为什么用 B+ 树?

  • 磁盘 IO 次数少
  • 适合范围查询
  • 查询稳定

3. 索引设计原则

3.1 哪些列需要建索引?

sql
-- ✅ 适合建索引
WHERE 条件列
ORDER BY 排序列
JOIN 连接列
高区分度列

-- ❌ 不适合建索引
低区分度(性别、状态)
频繁更新的列
很少查询的列

3.2 索引设计原则

sql
-- 1. 最左前缀原则
-- 索引 (a, b, c) 可以用于
WHERE a = 1         -- ✓ 使用 a
WHERE a = 1 AND b = 2  -- ✓ 使用 a, b
WHERE a = 1 AND b = 2 AND c = 3  -- ✓ 使用 a, b, c
WHERE b = 2         -- ✗ 无法使用
WHERE c = 3         -- ✗ 无法使用

-- 2. 区分度高的列放前面
-- 区分度 = COUNT(DISTINCT col) / COUNT(*)
-- 越高越好

-- 3. 避免索引失效
SELECT * FROM users WHERE age + 1 = 20;  -- ✗ 计算导致索引失效
SELECT * FROM users WHERE age = 19;     -- ✓ 可以使用索引

4. 索引优化实战

4.1 慢查询分析

sql
-- 开启慢查询日志
SHOW VARIABLES LIKE 'slow_query_log%';

-- 设置慢查询阈值
SET GLOBAL long_query_time = 1;

-- 查看慢查询
SHOW FULL PROCESSLIST;
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;

4.2 EXPLAIN 分析

sql
EXPLAIN SELECT * FROM users WHERE age > 18;

-- 关键字段:
-- type: 连接类型(const > eq_ref > ref > range > index > all)
-- key: 实际使用的索引
-- rows: 扫描行数
-- Extra: 额外信息(Using index, Using filesort 等)

4.3 常见问题

sql
-- ❌ 全表扫描
SELECT * FROM users WHERE name = 'Tom';

-- ✅ 使用索引
ALTER TABLE users ADD INDEX idx_name(name);
SELECT * FROM users WHERE name = 'Tom';

-- ❌ 索引失效
SELECT * FROM users WHERE SUBSTRING(name, 1, 3) = 'Tom';

-- ❌ OR 导致索引失效
SELECT * FROM users WHERE name = 'Tom' OR age = 18;

-- ✅ 改用 UNION
SELECT * FROM users WHERE name = 'Tom'
UNION ALL
SELECT * FROM users WHERE age = 18;

5. 索引优化技巧

5.1 覆盖索引

sql
-- 查询的列都在索引中,无需回表
-- 索引 (name, age, email)
SELECT name, age, email FROM users WHERE name = 'Tom';
-- ✓ Using index(覆盖索引)

5.2 索引下推

sql
-- 索引 (name, age)
SELECT * FROM users WHERE name LIKE 'T%' AND age = 18;
-- MySQL 5.6+ 自动在索引层面过滤 age

5.3 字符串索引

sql
-- 前缀索引(节省空间)
ALTER TABLE users ADD INDEX idx_email(email(10));

-- 全文索引
ALTER TABLE articles ADD FULLTEXT INDEX idx_content(content);
SELECT * FROM articles WHERE MATCH(content) AGAINST('MySQL');

6. 总结

原则说明
最左前缀按顺序使用索引列
避免函数索引列不参与计算
区分度高区分度高的列放前面
覆盖索引查询列在索引中
适量原则索引不是越多越好

📚 续篇:《MySQL 事务与锁》

> 学而时习之,不亦说乎?