--- title: "MySQL 索引优化实战:从 EXPLAIN 到索引设计" date: "2026-05-24" category: "数据库" tags: ["MySQL", "索引", "性能优化"] excerpt: "深入讲解 B+ 树索引结构、覆盖索引、索引下推、联合索引最左前缀原则。" --- ## 一、B+ 树索引结构 ### 1.1 为什么选择 B+ 树 MySQL InnoDB 存储引擎选择 B+ 树作为索引结构,原因如下: - 所有数据存储在叶子节点,非叶子节点只存键值 - 叶子节点通过双向链表连接,支持范围查询 - 树的高度通常在 2-4 层,IO 次数可控 ### 1.2 聚簇索引 vs 二级索引 **聚簇索引**:叶子节点存储完整行数据,一个表只能有一个。 **二级索引**:叶子节点存储主键值,查询需要回表。 ```sql -- 查看索引使用情况 EXPLAIN SELECT * FROM users WHERE email = 'test@example.com'; ``` ## 二、EXPLAIN 分析 ### 2.1 关键字段解读 | 字段 | 含义 | 期望值 | |------|------|--------| | type | 访问类型 | const > ref > range > index > ALL | | key | 实际使用的索引 | 不应为 NULL | | rows | 扫描行数估计 | 越小越好 | | Extra | 额外信息 | Using index 表示覆盖索引 | ### 2.2 常见优化案例 ```sql -- 案例1:避免索引失效 -- ❌ 函数导致索引失效 SELECT * FROM orders WHERE DATE(created_at) = '2026-01-01'; -- ✅ 范围查询 SELECT * FROM orders WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02'; ``` ## 三、索引设计原则 ### 3.1 最左前缀原则 联合索引 `(a, b, c)` 相当于创建了三个索引: - `(a)` - `(a, b)` - `(a, b, c)` ```sql CREATE INDEX idx_name_age_city ON users(name, age, city); -- 可以走索引 SELECT * FROM users WHERE name = '张三'; SELECT * FROM users WHERE name = '张三' AND age = 25; -- 不能走索引(跳过了 name) SELECT * FROM users WHERE age = 25; ``` ### 3.2 覆盖索引 查询的所有列都包含在索引中,避免回表: ```sql CREATE INDEX idx_email_name ON users(email, name); -- Using index(覆盖索引) SELECT email, name FROM users WHERE email = 'test@example.com'; ``` ## 四、总结 - 索引不是越多越好,每个索引都会增加写操作成本 - 定期分析慢查询日志 - 使用 `EXPLAIN` 验证索引是否被正确使用 - 联合索引遵循最左前缀原则