当前位置: 首页 > java >正文

SQL常见索引失效导致慢查询情况

数据库索引(尤其是B+树结构的索引)的作用类似于字典的目录,它需要是有序的。任何破坏这种有序性的操作,都可能使数据库优化器放弃使用索引,转而进行更低效的全表扫描。

索引失效的场景:

  1. 联合索引不满足最左匹配原则。
  2. 模糊查询最前面的为不确定匹配字符。
  3. 索引列参与了运算。
  4. 索引列使用了函数。
  5. 索引列存在类型转换。
  6. 索引列使用 is not null 查询。

1. 联合索引不满足最左匹配原则

原理:
联合索引(复合索引)是多个列上共同构建的一个索引,例如 INDEX idx_name_age (name, age)。这个索引的先按name排序,在name相同的情况下再按age排序。

最左匹配原则是指,查询条件必须从联合索引的最左列开始,并且不能跳过中间的列,才能充分利用该索引。

失效示例:

-- 表结构: user表,有联合索引 idx_name_age (name, age)-- 情况1:缺少最左列 `name`
SELECT * FROM user WHERE age = 25;-- 情况2:跳过了中间列 (假设索引是 (a, b, c))
SELECT * FROM user WHERE a = 'foo' AND c = 'bar'; -- 跳过了b,只能用a的部分索引

为什么失效?
因为索引的排序规则是先nameage。如果你只用age来查询,age在整个索引树里是无序的(因为先按name分了组),数据库无法利用索引的有序性进行快速查找,只能全表扫描。

特殊情况 - 索引下推(ICP):
在现代数据库(如MySQL 5.6+)中,即使跳过了中间列,如 WHERE a = 'foo' AND c = 'bar',数据库仍然会使用索引(a)进行初步筛选(a = 'foo'),然后再进行c = 'bar'的判断。这比完全不用索引好,但不如完全匹配所有列高效。

2. 模糊查询最前面的为不确定匹配字符

原理:
索引的有序性是基于列值的完整内容。如果查询条件的开头是通配符,就无法利用索引的有序性。

失效示例:

-- 假设在 `name` 字段上有索引
SELECT * FROM user WHERE name LIKE '%三'; -- 以'%'开头,索引失效
SELECT * FROM user WHERE name LIKE '%三%'; -- 以'%'开头,索引失效-- 不会失效的案例:
SELECT * FROM user WHERE name LIKE '张%'; -- 以确定字符开头,索引有效

为什么失效?
LIKE '张%'可以转化为范围查询(name >= '张' AND name < '丈'),可以利用索引的有序性快速定位。而LIKE '%三'要求查询所有以'三'结尾的值,这在索引树中是完全无序的,只能遍历所有数据。

3. 索引列参与了运算

原理:
索引中存储的是列的原始值。如果对列进行运算,数据库优化器无法将运算后的条件与索引中的原始值直接对应。

失效示例:

-- 假设在 `age` 字段上有索引
SELECT * FROM user WHERE age + 1 = 21; -- 索引失效
SELECT * FROM user WHERE YEAR(create_time) = 2023; -- 对列使用函数,也属于此类

为什么失效?
优化器不知道如何将 age + 1 这个条件反向计算出 age = 20,从而去索引里查找。它只能计算表中每一行的age + 1的值,然后判断是否等于21,这必然导致全表扫描。

解决方法:
将运算移到等号另一边,使用列的原生值。

SELECT * FROM user WHERE age = 20; -- 有效

4. 索引列使用了函数

原理:
这与“参与运算”的原理完全相同。索引存储的是原始值,而不是函数计算后的值。

失效示例:

-- 假设在 `create_time` (datetime类型) 字段上有索引
SELECT * FROM orders WHERE DATE(create_time) = '2023-10-26'; -- 索引失效
SELECT * FROM user WHERE LENGTH(name) = 2; -- 索引失效

为什么失效?
优化器无法通过 DATE(create_time) = '2023-10-26' 这个条件去索引树里查找,因为索引树是按 create_time 的原始值排序的,而不是按 DATE(create_time) 排序的。

解决方法:
使用范围查询代替函数。

SELECT * FROM orders
WHERE create_time >= '2023-10-26 00:00:00'
AND create_time < '2023-10-27 00:00:00'; -- 索引有效

5. 索引列存在类型转换

原理:
当操作符两边的数据类型不一致时,数据库会进行隐式类型转换(Implicit Cast),以使它们可以比较。这个转换过程相当于对列使用了函数。

失效示例:

-- 假设 `phone` 字段是字符串类型(varchar),并且有索引
SELECT * FROM user WHERE phone = 13800138000; -- 数据库会将phone列转为数字,索引失效-- 假设 `id` 字段是整数类型(int),并且有索引
SELECT * FROM user WHERE id = '123'; -- 数据库会将字符串'123'转为数字,索引仍然有效(原因看下方)

为什么失效?
WHERE string_column = number 会导致数据库调用 CAST(string_column AS SIGNED) 函数,将字符串列转为数字,这就回到了第4点“对列使用函数”,导致索引失效。

注意:
WHERE int_column = ‘123’ 通常不会导致索引失效,因为数据库会将字符串常量 ‘123’ 转换为数字 123,而不是对int_column列进行操作。关键是看转换是否作用于索引列本身

6. 索引列使用 is not null 查询

原理:
这个情况相对复杂,是否失效取决于数据库优化器的成本计算和表的具体情况。

  • 单列索引:在很多情况下,使用IS NOT NULL可以使用索引的,尤其是当索引本身具有高选择性(即NULL值很少)时。数据库可以通过遍历索引来跳过所有为NULL的条目。

  • 联合索引:如果联合索引的最左列查询条件是IS NOT NULL,由于其选择性太差(不符合最左匹配的精确查询),优化器可能认为全表扫描比回表查询大量数据更快,从而选择不使用索引。

总结:
IS NOT NULL 不一定会导致索引失效。优化器会评估使用索引的成本(需要回表的行数)和全表扫描的成本。如果表中大多数值都是NULL,用索引很快。如果几乎都不是NULL,那么用索引需要回表几乎所有的行,可能不如直接全表扫描。

失效示例(可能发生的情况):

-- 假设在 `address` 字段上有索引,但99%的记录都不为NULL
SELECT * FROM user WHERE address IS NOT NULL; -- 优化器可能选择全表扫描

总结与记忆技巧

可以将索引想象成一本按【姓氏+名字】排序的通讯录。

  1. 最左匹配:你无法快速找出所有叫“伟”的人,因为通讯录是先按“姓”排序的。

  2. 模糊前缀:你无法快速找出所有姓氏以“王”字结尾的人。

  3. 列运算/函数:你想找“姓氏长度等于1”的人,通讯录的目录帮不了你,你得自己翻。

  4. 类型转换:你把要找的“中文姓名”用拼音报出来,目录也无法直接匹配。

  5. IS NOT NULL:你想找出所有“电话号码不为空”的人。如果只有少数人没电话,用目录很快(跳过没电话的)。如果人人都有电话,用目录反而慢(得把所有人的号码看一遍),不如直接按顺序翻名单。

http://www.xdnf.cn/news/20042.html

相关文章:

  • mysql集群部署(Mysql Group Replication)
  • 如何将数据从 Infinix 转移到 Infinix ?
  • 生活在数字世界:一份人人都能看懂的网络安全生存指南
  • @Percona XtraBackup 进行 MySQL 备份恢复
  • Day35 TCP实时聊天程序实现(多线程)
  • 3 步搞定顶刊科研插图!用 GPT-5 反推提示词,Nano Banana 模型一键出图,附实操演示
  • 国内外开源大模型 LLM整理
  • 2025 年高教社杯全国大学生数学建模竞赛E 题 AI 辅助智能体测完整成品 思路 模型 代码 结果分享!全网首发高质量!!!
  • 【LeetCode】22、括号生成
  • 算法之二叉树
  • 【Python基础】 15 Rust 与 Python 基本类型对比笔记
  • C# 修改基类List中某一元素的子类类型
  • 11 月广州见!AUTO TECH China 2025 汽车内外饰展,解锁行业新趋势
  • Leetcode—3516. 找到最近的人【简单】
  • ORA-12547: TNS:lost contact
  • 算法模板(Java版)_字符串、并查集和堆
  • matlab版本粒子群算法(PSO)在路径规划中的应用
  • PDF批量加盖电子骑缝章的方法!高效办公必备
  • 每天学习一点点之湿敏等级以及肖特基二极管
  • C#之LINQ
  • wps的excel如何转为谷歌在线表格
  • testng.xml
  • Opencv: cv::LUT()深入解析图像块快速查表变换
  • sqlserver2008导入excel表数据遇到的问题
  • 无线路由器:从家庭上网到智慧互联的核心设备
  • 人工智能学习:LR和SVM的联系与区别?
  • AI助力软件UI概念设计:卓伊凡收到的客户设计图引发的思考
  • Node.js轻松生成动态二维码
  • C++对象模型的底层逻辑
  • 【数据分享】土地利用矢量shp数据分享-福建