【MySQL篇|第一篇】索引失效的情况

1. 引言

在 MySQL 中,索引是提升查询性能的关键手段,但索引并非总能生效。很多情况下,即使表上建立了合适的索引,SQL 语句依然会走全表扫描,导致查询变慢。理解索引失效的常见场景,有助于写出更高效的 SQL,也能帮助排查线上慢查询问题。下面是一些常见的索引失效的情况。

2. 索引失效的常见情况

下面列举 MySQL 索引失效的典型场景,并给出对应的示例和规避建议。

2.1 对索引列使用函数或表达式

当查询条件中对索引列使用了函数、运算或类型转换时,MySQL 通常无法使用索引,因为索引中存储的是原始值,而不是函数处理后的结果。

sql 复制代码
-- 失效:对索引列使用函数
SELECT * FROM user WHERE DATE(create_time) = '2024-01-01';

-- 失效:对索引列进行运算
SELECT * FROM user WHERE age + 1 = 30;

-- 建议改写为:
SELECT * FROM user WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';
SELECT * FROM user WHERE age = 29;

2.2 隐式类型转换

当索引列是字符串类型,而查询条件传入的是数值类型(或反之),MySQL 会进行隐式类型转换,导致索引失效。这是一个很隐蔽的点,在实际的开发中需要格外的注意。

sql 复制代码
-- 假设 phone 字段是 varchar 类型且有索引
-- 失效:传入数值,发生隐式转换
SELECT * FROM user WHERE phone = 13800138000;

-- 建议改写为:
SELECT * FROM user WHERE phone = '13800138000';

2.3 模糊查询以通配符开头

当 LIKE 查询以 % 或 _ 开头时,索引无法用于前缀匹配,只能全表扫描。

sql 复制代码
-- 失效:以通配符开头
SELECT * FROM user WHERE name LIKE '%张';

-- 有效:以固定前缀开头
SELECT * FROM user WHERE name LIKE '张%';

2.4 联合索引未遵循最左前缀原则

联合索引 (a, b, c) 遵循最左前缀原则。如果查询条件没有包含最左列 a,索引通常无法使用。

sql 复制代码
-- 假设联合索引 idx(a, b, c)
-- 失效:未使用最左列 a
SELECT * FROM t WHERE b = 1 AND c = 2;

-- 有效:包含最左列 a
SELECT * FROM t WHERE a = 1 AND b = 1;

2.5 使用 OR 连接非索引列

当 OR 连接的多个条件中,只要有一个列没有索引,整个查询就可能放弃使用索引。

sql 复制代码
-- 假设 name 有索引,age 无索引
-- 失效:age 无索引导致整体无法走索引
SELECT * FROM user WHERE name = '张三' OR age = 30;

-- 建议改写为 UNION 或为 age 也建立索引
SELECT * FROM user WHERE name = '张三'
UNION
SELECT * FROM user WHERE age = 30;

2.6 对索引列进行排序或分组时使用不当

当 ORDER BY 或 GROUP BY 的字段顺序与索引顺序不一致,或排序方向不一致时,索引可能无法用于排序优化。

sql 复制代码
-- 假设联合索引 idx(a, b)
-- 失效:排序顺序与索引不一致
SELECT * FROM t WHERE a = 1 ORDER BY b DESC;

-- 有效:与索引顺序一致
SELECT * FROM t WHERE a = 1 ORDER BY b ASC;

2.7 使用不等于、NOT IN、IS NOT NULL

对于范围查询和否定条件,MySQL 优化器往往认为走索引的代价高于全表扫描,从而放弃索引。

sql 复制代码
-- 失效:不等于条件
SELECT * FROM user WHERE status != 1;

-- 失效:NOT IN
SELECT * FROM user WHERE id NOT IN (1, 2, 3);

-- 失效:IS NOT NULL
SELECT * FROM user WHERE email IS NOT NULL;

2.8 数据量过小或优化器选择全表扫描

当表中数据量很小,或者索引选择性很低(如性别字段只有两个值)时,MySQL 优化器可能认为全表扫描比走索引更快,从而主动放弃索引。

sql 复制代码
-- 即使有索引,优化器也可能选择全表扫描
SELECT * FROM user WHERE gender = '男';

3. 如何确认索引是否生效

使用 EXPLAIN 关键字可以查看 SQL 的执行计划,重点关注 type 和 key 字段。

sql 复制代码
EXPLAIN SELECT * FROM user WHERE name = '张三';
  • type:如果为 ALL,说明走了全表扫描;如果为 ref、range、index 等,说明使用了索引。
  • key:显示实际使用的索引名称;如果为 NULL,说明没有使用索引。

4. 总结

索引失效的原因多种多样,核心在于理解 MySQL 优化器的工作方式以及索引的数据结构。日常开发中,建议遵循以下原则:避免对索引列使用函数和运算、保持字段类型一致、遵循最左前缀原则、谨慎使用 OR 和否定条件,并善用 EXPLAIN 验证执行计划。只有让索引真正生效,才能充分发挥数据库的性能优势。对一些少量数据我们也不要胡用索引,我们要优先选择查询更快的方案。

相关推荐
星恒随风13 小时前
MySQL 从零入门(六):复合查询详解——多表查询、自连接、子查询与 UNION
linux·服务器·数据库·mysql
无奈笑天下15 小时前
银河麒麟上lsblk命令教程详解
linux·运维·服务器·数据库
xiaoye-duck16 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
AllData公司负责人16 小时前
AI大模型数据开发底座系列说明书:AllData集成DB-GPT开源项目建设 AI 多模态分析底座,涵盖模态解析/智能大屏/MCP 扩展!
大数据·数据库·人工智能·gpt·数据采集·数据接入·模态解析
隔窗听雨眠17 小时前
MySQL进程卡死的定位思路与恢复实践
mysql·故障排查·数据库运维
2601_9679873717 小时前
Sqlite参考手册
数据库·python·sqlite·api·教程
隔窗听雨眠17 小时前
Oracle日志体系分层结构与故障定位路径
数据库·oracle·故障排查
为思念酝酿的痛17 小时前
C语言操作MySQL
数据库·mysql
ShineWinsu18 小时前
对于Qt:QTabWidget常见属性的解析
开发语言·数据库·c++·qt·浏览器·qtabwidget·标签页
星恒随风1 天前
MySQL 从零入门(五):MySQL 内置函数详解——日期、字符串、数学函数与实战案例
linux·数据库·笔记·sql·mysql