数据库索引为什么会失效?从执行计划理解 SQL 性能优化

在实际开发过程中,很多工程师都会遇到类似的问题:明明已经给数据库字段建立了索引,但查询速度并没有明显提升,甚至在数据量增长后,原本流畅的 SQL 开始出现明显延迟。

很多人第一反应是"索引没有创建成功"或者"数据库性能不够",但真正的问题往往出现在 SQL 的执行方式上。索引并不是创建之后就一定会被使用,它只是数据库优化器提供的一种查询路径选择。当优化器判断全表扫描成本更低时,即使存在索引,也可能直接放弃使用。

理解索引失效的原因,需要从数据库如何执行 SQL 开始。

索引到底解决了什么问题

数据库中的数据通常存储在磁盘或内存页中。如果没有索引,当执行类似下面的查询:

sql 复制代码
SELECT * FROM user WHERE username = 'Tom';

数据库需要逐行检查 user 表中的数据,判断 username 是否等于 Tom。

当表中只有几千条数据时,这种方式影响并不明显。但如果数据量达到百万级甚至千万级,全表扫描意味着数据库需要读取大量无关数据,查询时间会随着数据规模增长。

索引的作用,就是建立一个额外的数据结构,让数据库可以快速定位目标数据。

以 MySQL 常见的 B+Tree 索引为例,它并不会保存完整的数据记录,而是按照索引字段进行排序,并保存指向真实数据位置的引用。

查询时,数据库不需要从第一行开始查找,而是通过树结构快速定位目标范围,再读取对应的数据。

这也是为什么建立合理索引后,查询效率可以从秒级降低到毫秒级。

但是,索引并不是免费的。

索引需要额外占用存储空间,同时在新增、修改、删除数据时,数据库还需要维护索引结构。因此,索引设计的核心不是"越多越好",而是让数据库在查询成本和维护成本之间达到平衡。

为什么有索引却没有使用

最常见的问题之一,就是 SQL 写法导致索引无法发挥作用。

例如:

sql 复制代码
SELECT * FROM user WHERE name LIKE '%Tom';

虽然 name 字段存在索引,但这个查询通常无法利用普通 B+Tree 索引。

原因在于索引是按照从左到右的顺序排列的。当查询条件以固定字符串开头时,例如:

sql 复制代码
SELECT * FROM user WHERE name LIKE 'Tom%';

数据库可以快速定位以 Tom 开头的数据范围。

但如果使用:

sql 复制代码
LIKE '%Tom'

数据库无法提前确定 Tom 所在的位置,只能扫描大量数据,因此索引效果会明显下降。

类似的问题还包括对索引字段进行函数处理:

sql 复制代码
SELECT * FROM user 
WHERE DATE(create_time) = '2026-09-29';

这里虽然 create_time 建立了索引,但 DATE() 函数改变了字段本身的值,数据库无法直接通过索引定位。

更合理的写法是:

sql 复制代码
SELECT * FROM user
WHERE create_time >= '2026-09-29 00:00:00'
AND create_time < '2026-09-30 00:00:00';

这种范围查询可以继续利用索引结构。

联合索引为什么容易踩坑

实际业务中,经常需要根据多个字段查询。

例如用户订单查询:

ini 复制代码
SELECT *
FROM orders
WHERE user_id = 1001
AND status = 'paid';

很多开发者会建立联合索引:

scss 复制代码
CREATE INDEX idx_user_status
ON orders(user_id, status);

这个设计通常是合理的。

但是联合索引遵循"最左匹配原则"。

如果查询变成:

ini 复制代码
SELECT *
FROM orders
WHERE status = 'paid';

数据库无法直接利用 idx_user_status,因为索引第一列是 user_id,而查询没有提供 user_id 条件。

可以把联合索引理解成一本按照"用户编号→订单状态"排序的通讯录。

如果你知道用户编号,可以快速找到对应区域,再继续查状态。

但如果你只知道状态,却不知道用户编号,就无法直接定位。

因此,在设计联合索引时,需要结合真实查询场景,而不是简单按照字段数量创建。

如何通过执行计划判断索引是否生效

很多开发者优化 SQL 时,只关注执行结果,却忽略数据库实际采用了什么方式。

在 MySQL 中,可以通过:

sql 复制代码
EXPLAIN SELECT *
FROM user
WHERE username='Tom';

查看执行计划。

其中几个关键字段非常重要。

type 表示访问类型。

常见类型包括:

  • ALL:全表扫描
  • index:扫描整个索引
  • range:范围查询
  • ref:通过索引快速匹配
  • const:常量级查询

通常情况下,ALL 是需要重点关注的情况,因为它意味着数据库正在扫描大量数据。

key 字段表示实际使用的索引。

如果建立了索引,但 key 为空,说明优化器没有选择该索引。

rows 字段表示预计扫描的数据数量。

例如:

makefile 复制代码
rows: 5000000

说明数据库可能需要检查五百万行数据。

而优化后的查询:

makefile 复制代码
rows: 10

则意味着索引已经帮助数据库缩小了搜索范围。

数据量小的时候,索引可能反而没有优势

一个容易被忽视的问题是,小表不一定需要索引。

例如:

bash 复制代码
user_config
-------------
id
key
value

如果表中只有几十条配置数据,即使没有索引,数据库扫描几十行数据的成本也非常低。

此时建立索引不仅不会明显提升性能,反而增加了维护成本。

数据库优化不是简单追求更多索引,而是根据业务规模和访问模式进行设计。

真正需要优化的,通常是高频访问、大数据量、查询复杂的核心表。

索引优化不仅是数据库问题

很多 SQL 性能问题,并不是数据库单方面造成的。

例如接口一次查询大量无用字段:

sql 复制代码
SELECT *
FROM user;

可能比:

sql 复制代码
SELECT id,name
FROM user;

消耗更多资源。

因为数据库需要读取更多数据,网络也需要传输更多内容。

再比如分页查询:

vbnet 复制代码
SELECT *
FROM article
ORDER BY id
LIMIT 100000,20;

随着偏移量增加,数据库需要先扫描大量数据,再丢弃前面的结果。

对于大规模数据,可以采用基于游标的分页:

vbnet 复制代码
SELECT *
FROM article
WHERE id > 100000
ORDER BY id
LIMIT 20;

这种方式可以让数据库继续利用索引快速定位。

因此,数据库性能优化往往涉及数据结构、SQL 写法、业务逻辑以及系统架构多个层面。

如何建立更加合理的索引体系

实际项目中,索引设计应该从业务查询出发。

开发前期,可以根据主要业务场景整理高频 SQL,分析哪些字段经常作为查询条件、排序条件和关联条件。

对于频繁查询但数据变化较少的字段,可以考虑建立索引。

对于区分度低的字段,需要谨慎处理。

例如:

markdown 复制代码
gender
---------
男
女

如果一个用户表有几千万数据,通过 gender 查询通常无法有效减少扫描范围,因为每个类别的数据量都很大。

而用户手机号、订单编号这类唯一性较高的字段,更适合作为索引。

同时,索引建立后也需要持续观察。

随着业务发展,数据分布会发生变化,过去有效的索引可能不再适合当前环境。

数据库优化不是一次性的工作,而是伴随业务增长持续调整的过程。

总结

索引是数据库性能优化中最重要的工具之一,但它并不是简单的"加一个索引就变快"。

真正影响索引效果的,是 SQL 写法、数据分布、查询模式以及数据库优化器的判断。

理解执行计划,比盲目增加索引更加重要。

当数据库出现慢查询时,与其立即增加更多索引,不如先分析 SQL 的实际执行路径,找到真正消耗资源的位置。

好的数据库设计,不是让所有查询都使用索引,而是在合适的场景下,让数据库选择最合理的访问方式。

相关推荐
此时不提桶,更待何时1 小时前
03-04-B-连接池与DB治理面试与生产事故实战
mysql·面试
Andreapiki2 小时前
风险审计校招技术栈拆解:SQL、Python、Power BI在2026届JD中的真实权重
数据库·python·sql
dogeyi2 小时前
数据库操作(1)
mysql
JosieBook2 小时前
【数据库】MySQL 实战精通系列 · 第8篇:慢查询治理与性能优化实战
数据库·mysql·性能优化
这个DBA有点耶3 小时前
分区表深入:分区裁剪失效的6种场景、分区锁机制与维护实战
数据库·mysql·dba
weixin_404551246 小时前
Oracle SQL 优化知识库:构建方案与价值评估
数据库·sql·oracle
我的愿望是成为富婆8 小时前
HR数字化校招技术栈拆解:SQL、Excel、Power BI和AI工具在2026届JD中的真实权重
人工智能·sql·excel
做运维的阿瑞9 小时前
一张用户表串懂 MySQL 的库、表、列、行、主键
数据库·sql·mysql·oracle