MySQL 索引优化实战
B+Tree 索引原理
MySQL InnoDB 引擎默认使用 B+Tree 索引结构:
- 所有数据存放在叶子节点
- 非叶子节点只存键值,不存数据
- 叶子节点通过双向链表连接,支持高效范围查询
- 树的高度通常 2-3 层,每次查询只需 2-3 次磁盘 I/O
索引类型及创建
-- 主键索引(聚簇索引,自动创建) ALTER TABLE article ADD PRIMARY KEY (id);
-- 唯一索引 CREATE UNIQUE INDEX idx_slug ON article (slug);
-- 普通索引 CREATE INDEX idx_title ON article (title);
-- 联合索引(重要!) CREATE INDEX idx_cat_time ON article (category_id, publish_time);
最左前缀原则
联合索引 (a, b, c) 相当于创建了三个索引:
- (a) 可用
- (a, b) 可用
- (a, b, c) 可用
- (b, c) 不可用 — 跳过了 a
CREATE INDEX idx_abc ON test (a, b, c);
-- 走索引 SELECT * FROM test WHERE a = 1; SELECT * FROM test WHERE a = 1 AND b = 2;
-- 不走索引 SELECT * FROM test WHERE b = 2; SELECT * FROM test WHERE c = 3 ORDER BY b;
EXPLAIN 执行计划
EXPLAIN SELECT * FROM article WHERE category_id = 5 ORDER BY publish_time DESC;
关键字段解读:
| 字段 | 含义 | 理想值 |
|---|---|---|
| type | 访问类型 | const > ref > range > index > ALL |
| key | 使用的索引 | 非 NULL |
| rows | 预估扫描行数 | 越小越好 |
| Extra | 额外信息 | Using index(覆盖索引最好) |
需要警惕的 Extra 值:
- Using filesort — 需要额外排序,考虑加索引
- Using temporary — 使用了临时表
- Using where — 有过滤条件但没用到索引
常见索引优化场景
- WHERE 条件列加索引
- ORDER BY 列加索引(避免 filesort)
- JOIN 关联列加索引
- 高选择性的列优先(性别不适合单独索引)
- 覆盖索引:查询列全部在索引中,无需回表
-- 覆盖索引示例 CREATE INDEX idx_cover ON article (category_id, publish_time, title); SELECT category_id, publish_time, title FROM article WHERE category_id = 5; -- Using index — 直接从索引返回,无需回表
慢查询排查
-- 开启慢查询日志 SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1;
优化步骤:
- 查看慢查询日志,定位慢 SQL
- EXPLAIN 分析执行计划
- 添加合适的索引
- 优化 SQL 写法(避免 SELECT *、优化 JOIN 顺序、用 EXISTS 替代 IN)
- 考虑读写分离、分库分表