在数据库开发与优化中,索引是提升MySQL查询性能的核心手段之一。合理创建和使用索引,能显著减少数据检索的时间;但如果索引设计不当,不仅无法优化查询,还会增加数据插入、更新和删除的开销。本文结合实战场景,详细梳理索引的添加场景、联合索引的使用注意事项、索引失效的常见情况,同时补充高频面试知识点,帮助开发者高效掌握索引优化技巧。
一、哪些情况下,需要给字段添加索引?
索引的核心作用是加速数据查找,以下场景中,给对应字段添加索引能带来明显的性能提升:
- 数据检索的条件字段
当字段频繁出现在WHERE子句中作为查询条件时,添加索引可以避免全表扫描,快速定位符合条件的数据。 - 聚合函数的聚合字段
对于COUNT()、SUM()、MAX()等聚合函数作用的字段,添加索引能优化聚合计算的效率,减少数据库遍历数据的成本。 - 排序操作的排序字段
当使用ORDER BY对字段进行排序时,索引可以帮助数据库直接按照索引顺序返回数据,避免额外的排序操作。 - 需要防止回表的字段
为了减少"回表查询"的开销,可以针对性地创建索引,让查询结果直接从索引中获取,无需再访问数据行。 - 关联查询的关联字段
在多表JOIN操作中,关联字段(如外键字段)添加索引,能大幅提升关联查询的效率,避免多表匹配时的全表扫描。
| 考虑加索引的情况 |
|---|
| 数据检索时在条件字段添加索引 |
| 聚合函数对聚合字段添加索引 |
| 对排序字段添加索引 |
| 为了防止回表添加索引 |
| 关联查询在关联字段添加索引等 |
二、使用联合索引的三大注意事项
联合索引(复合索引)是包含多个字段的索引,相比单字段索引,更适合多条件组合查询的场景,但使用时需遵循以下原则:
- 优先为常组合查询的列创建联合索引
如果多个字段经常同时出现在WHERE子句中作为查询条件,建议创建联合索引而非多个单字段索引。例如,用户表中经常根据name和age组合查询,那么创建(name, age)联合索引,比分别创建name和age的单索引更高效。 - 严格遵循"最左前缀匹配"原则
联合索引的生效遵循最左前缀匹配 规则,即查询条件必须从索引的最左列开始匹配,中间跳过列会导致索引失效。
举个例子:若创建了(a, b, c)联合索引,那么:- 支持索引的查询条件:
WHERE a = 1、WHERE a = 1 AND b = 2、WHERE a = 1 AND b = 2 AND c =3 - 不支持索引的查询条件:
WHERE b = 2、WHERE b = 2 AND c =3
- 支持索引的查询条件:
- 合理利用覆盖索引
覆盖索引是指查询的字段恰好包含在索引中,无需回表即可获取数据。设计联合索引时,可以将查询中需要返回的字段加入索引,实现"索引即数据"。例如,经常通过a字段查询b字段的值,创建(a, b)联合索引,查询时直接从索引中读取b的值,避免回表操作。
| 联合索引的注意项 |
|---|
| 经常同时出现在条件中的列可以创建联合索引 |
| 最左前缀匹配 |
| 合理使用覆盖索引 |
三、这些场景下,即使有索引也会失效
在实际开发中,经常会遇到"字段加了索引,但查询依然走全表扫描"的情况,以下是索引失效的常见场景及原因:
- 对字段进行函数操作
索引树中存储的是字段的原始值,若查询时对字段使用函数(如DATE_FORMAT(create_time, '%Y-%m') = '2025-01'),数据库无法直接匹配索引树中的值,只能放弃索引走全表扫描。 - 字段类型的隐式转换
MySQL在执行查询时,若条件中字段类型与传入值类型不匹配,会触发隐式类型转换,这一过程相当于对字段进行了函数操作,导致索引失效。例如,字符串类型的字段phone,使用WHERE phone = 13800138000查询(传入数值型),会触发隐式转换,索引失效。 - 以%开头的模糊查询
模糊查询中,LIKE '%xxx'的写法会导致索引失效,因为MySQL无法确定索引的匹配起点;而LIKE 'xxx%'可以正常使用索引,因为查询条件匹配索引的前缀。 - 匹配数据比例过大的范围查询
当范围查询(如>、<、BETWEEN)匹配的数据量占表数据总量比例过高时,MySQL优化器会评估索引的性价比,若全表扫描的效率更高,会放弃使用索引。例如,查询一张百万级数据表中"年龄大于10岁"的数据,由于匹配数据量过大,优化器可能选择全表扫描。 - 对字段进行计算操作
若查询条件中对字段进行算术运算(如WHERE price * 0.8 < 100),会导致索引失效。原因与函数操作类似,计算后的值未存储在索引树中,无法直接匹配。
| 不合理使用索引的场景 |
|---|
| 函数操作 |
| 隐式转换 |
| 模糊查询 |
| 匹配的数据比例过大的范围查询 |
| 计算操作 |
四、MySQL索引高频面试知识点补充
(一)MySQL有哪些常见的索引类型?
- 普通索引:最基础的索引类型,无唯一性约束,仅用于加速查询。
- 唯一索引:索引列的值必须唯一,允许为空值(与主键索引的区别是主键索引不允许为空)。
- 联合索引:包含多个字段的索引,遵循最左前缀匹配原则。
- 前缀索引 :针对字符串类型字段,截取字段的前N个字符创建索引,适用于字段长度过长(如
VARCHAR(255))或BLOB/TEXT类型字段(无法整列创建索引)的场景。 - 全文索引 :用于在大文本字段中快速检索关键字,适用于
CHAR、VARCHAR、TEXT类型字段。
(二)二级索引的叶子节点存放的是什么?
二级索引(非主键索引)的叶子节点存储的是索引值和对应的主键ID。查询时,先通过二级索引找到主键ID,再通过主键索引回表查询完整数据行。
(三)为什么唯一索引的更新不能使用Change Buffer?
Change Buffer的核心作用是,将普通索引的更新操作(插入、更新、删除)先缓存到内存中,待后续时机批量写入磁盘,减少随机磁盘I/O。
而唯一索引的更新,必须先将数据页读入内存,验证数据的唯一性(防止违反唯一约束)。既然数据页已经在内存中,直接更新内存中的数据页效率更高,因此不需要使用Change Buffer。
(四)主键索引和非主键索引的区别
| 对比维度 | 主键索引 | 非主键索引 |
|---|---|---|
| 叶子节点内容 | 直接存储完整数据行 | 存储索引值 + 主键ID |
| 查询性能 | 无需回表,查询速度快 | 需要回表(覆盖索引除外),速度相对较慢 |
| 更新影响 | 更新可能改变数据行的物理位置 | 更新仅影响索引本身,不影响数据行位置 |
(五)什么是覆盖索引?
覆盖索引是指查询的字段全部包含在索引中,无需回表即可获取所有需要的数据。例如,(a, b)联合索引,执行SELECT b FROM table WHERE a = 1时,直接从索引中读取b的值,无需回表,这就是覆盖索引的应用。
总结
索引优化是一个"因地制宜"的过程,没有万能的索引设计方案。开发者需要结合业务查询场景,合理选择索引类型、规划索引字段,同时规避索引失效的坑,才能让索引真正成为提升MySQL性能的利器。