详解MySQL索引
索引是 MySQL 性能优化的核心要素之一,合理的索引设计可以大幅提升查询效率。下面我将从索引原理、类型、使用场景到优化策略,系统性地讲解 MySQL 索引。
1. 索引的基本概念
1. 什么是索引?
索引是存储引擎用来快速查找数据的数据结构,类似于书籍的目录。它通过特定的算法(如B+树)对表中的一列或多列值进行排序存储,从而加速数据检索。
2. 索引的优缺点
-
优化:
-
大幅提高查询速度。
-
加速表连接操作。
-
保证数据唯一性(唯一索引)。
-
优化排序和分组操作。
-
-
缺点:
-
占用额外存储空间。
-
降低数据写入速度(增删改需要维护索引)。
-
增加优化器选择负担。
-
2. MySQL 索引类型
1. 按数据结构分类
索引类型 | 描述 | 适用场景 |
---|---|---|
B+Tree索引 | InnoDB默认索引类型,平衡多路搜索树 | 等值查询、范围查询、排序 |
Hash索引 | 基于哈希表实现,精确匹配效率极高,InnoDB 自动为频繁访问的二级索引创建哈希索引,加速等值查询,完全自动管理,无需配置 | 内存表的等值查询 |
Full-text | 全文索引,基于倒排索引实现 | 文本内容的搜索 |
R-Tree | 空间索引,用于地理数据 | GIS数据查询 |
2. 按功能特性分类
索引类型 | 描述 |
---|---|
普通索引 | 最基本的索引,无特殊约束 |
唯一索引 | 保证列值的唯一性,允许NULL值 |
主键索引 | 特殊的唯一索引,不允许NULL值,每个表只能有一个 |
组合索引 | 多个列组合建立的索引 |
覆盖索引 | 查询的列都包含在索引中,无需回表 |
前缀索引 | 只对列的前N个字符建立索引,节省空间 |
3. B+Tree 索引深度解析
1. B+Tree 结构特点
-
多路平衡查找树:保持数据有序且查询路径长度均衡
-
非叶子节点只存键值:不存储实际数据,存储索引键值+指针
-
叶子节点形成链表:便于范围查询和全表扫描
-
存储索引列的值
-
对应记录的主键值
-
事务ID、回滚指针等元数据
数据只存在叶子节点:保证查询稳定性
-
2. InnoDB 的 B+Tree 实现
-
聚簇索引:主键索引的叶子节点存储完整行数据
-
二级索引:二级索引也称为非聚簇索引,是除主键索引外的所有索引的统称。与聚簇索引(主键索引)不同,二级索引的叶子节点不直接包含完整行数据。非主键索引的叶子节点存储主键值(需要回表查询)。
-
二级索引的工作机制:
-
查询流程(回表示例)
-- 假设有二级索引 idx_name(name) SELECT * FROM users WHERE name = '张三';
执行步骤:
-
在 idx_name 索引树查找"张三"
-
找到对应的主键值(如 id=5)
-
用主键值到聚簇索引查找完整记录
-
返回所有列数据
-
-
覆盖索引优化
当查询列都包含在二级索引中时,可避免回表:-- 索引 idx_name_age(name, age) SELECT name, age FROM users WHERE name = '张三';
此时只需访问二级索引即可获取所需数据。
-
-
特性 | 二级索引 | 聚簇索引(主键索引) |
---|---|---|
存储内容 | 索引列+主键值 | 完整数据行 |
数量 | 一个表可有多个 | 每个表只有一个 |
查询方式 | 可能需要回表 | 直接获取数据 |
叶子节点 | 存储指向主键的引用 | 存储实际数据记录 |
4. 索引使用最佳实践
1. 索引设计原则
-
选择性高的列优先:区分度高的列(如ID)比性别更适合建索引
-
常用查询条件列:WHERE、JOIN、ORDER BY 涉及的列
-
避免过度索引:一般表不超过5-6个索引
-
组合索引列顺序:区分度高的列在前,常用查询列在前
2. 最左前缀原则
对于组合索引 (a,b,c):
-- 能使用索引的情况
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3
WHERE a = 1 AND c = 3 -- 部分使用(a)-- 不能使用索引的情况
WHERE b = 2
WHERE c = 3
WHERE b = 2 AND c = 3
3. 索引失效场景
-
使用函数或运算:
WHERE YEAR(create_time) = 2023
-
隐式类型转换:
WHERE name = 123
(name是字符串类型) -
前导模糊查询:
WHERE name LIKE '%张'
-
OR条件不当:
WHERE a=1 OR b=2
(当a、b未都有索引时) -
索引列使用NOT:
WHERE NOT status=1
5. 索引优化技巧
1. EXPLAIN 分析
Explain详解跳转
EXPLAIN SELECT * FROM users WHERE name = '张三';
关注关键字段:
-
type:从优到差 system > const > eq_ref > ref > range > index > ALL
-
key:实际使用的索引
-
rows:预估扫描行数
-
Extra:
Using index
(覆盖索引)、Using filesort
(需要额外排序)
2. 索引优化策略
-
避免SELECT *:只查询需要的列,提高覆盖索引概率
-
使用索引提示:
FORCE INDEX
、USE INDEX
-
定期分析表:
ANALYZE TABLE
更新索引统计信息 -
长字符列使用前缀索引:
ALTER TABLE t ADD INDEX idx_name(name(10))
6. 特殊索引场景
1. 覆盖索引优化
-- 创建组合索引
ALTER TABLE orders ADD INDEX idx_user_product(user_id, product_id);-- 查询可以完全使用索引
SELECT user_id, product_id FROM orders WHERE user_id = 100;
2. 索引下推(ICP)
MySQL 5.6+特性,将WHERE条件过滤下推到存储引擎层:
-- 组合索引(a,b)
SELECT * FROM t WHERE a = 'xxx' AND b LIKE '%yyy%';
-- 传统:先回表再过滤b条件
-- ICP:在索引层先过滤b条件再回表
3. MRR优化
多范围读取优化,减少随机IO:
-- 二级索引查询后,按主键排序再回表
SELECT * FROM t WHERE a > 100 AND a < 1000;
7. 索引维护
1. 查看索引信息
SHOW INDEX FROM table_name;
2. 索引碎片整理
-- InnoDB表重建(会锁表)
ALTER TABLE table_name ENGINE=InnoDB;-- 优化表(5.7+在线DDL)
OPTIMIZE TABLE table_name;
3. 索引监控
-- 查看索引使用情况
SELECT * FROM sys.schema_index_statistics;
SELECT * FROM sys.schema_unused_indexes;
8. 总结
合理设计和使用索引是MySQL性能优化的关键。需要根据实际查询模式、数据分布和业务需求来制定索引策略,并持续监控和调整。记住:最好的索引是那些被实际查询使用的索引,而非数量最多的索引。