为何在数据库查询时,不利用索引来提升检索效率?
- 内容介绍
- 文章标签
- 相关推荐
在实际项目中。许多开发者都会遇到“为什么我的查询没有走索引?”这类问题,说起来,往往因为一次慢查询导致整个应用响应变慢、数据库连接数飙升甚至服务宕机。
一、常见导致不走索引的原因
-
查询条件不包含索引列
如果WHERE子句里根本没有用到被建好的列,数据库自然不会去利用它们。
💡 痛点:你经常看到慢日志里写着“Table scan”,却忘记检查是否遗漏了关键字段。老实说,
-
对索引列使用函数或表达式
例如
WHERE SUBSTRING='A娱乐'或WHERE age+1=30。数据库需要先对每行执行该函数。再比较结果,无法直接定位。
💡 痛点:业务需求变更后把业务字段包装成了表达式,却不知道这会让查询失效。
-
数据量过小
当表只有几百条记录时全表扫描的成本比走索引更低。
💡 痛点:你为一个极小的数据集加了复杂的复合索引,却发现反而更慢。
-
数据分布不均匀
某些值出现频率极高,导致扫描大量行。
💡 痛点:业务日志表里某个状态字段几乎全是同一个值,任何基于该字段的查询都快要变成扫表。
-
统计信息过时或失效
DROP INDEX / CREATE INDEX或者执行
MIGRATE STATISTICS / ANALYZE TABLE数据库根据统计决定是否使用索引;如果统计错误就会误判为全扫。
💡 痛点:最近一次大批量插入后没有刷新统计,导致旧计划被缓存继续走错误方法。
-
强制不使用索引的提示或语法(如
NOSORT/MATCHEDONLY/FORCESEEK) -
MULTI‑OR 条件无效化
`WHERE a=1 OR b=2` 如果a和b分别都有独立有效的单列索引。但调整器认为两者组合成本高,就会退回全扫。
- LIKE 开头通配符导致无法利用前缀匹配** *
- IDLE 或破损的物理结构** *
二、使用者痛点拆解与排查步骤
- 1️⃣ 查询慢但未显式报错: SQL执行时间>5s → 看EXPLAIN Plan,看是否有Index Used标记;若无则说明走了全表扫描,
- 2️⃣ 高并发下CPU/IO飙升: 监控CPU & I/O → 排查是否有大量重复扫描同一大表;若是则需考虑添加覆盖/聚簇等优先级更高的Index。
- 3️⃣ 长事务占用锁资源: 长时间未提交的 SELECT 在某些DBMS上会升级为共享锁;若是大范围扫描,会阻塞写操作。
再看排查技巧,
- COLUMN IS NULL CHECK: 在调试之前确认是否有NULL值被忽略——NULL通常不会被普通B‑Tree Index覆盖。
- AUTO ANALYZE 设置: 开启自动统计更新,让DBMS自行维护最新分布信息。
- EVALUATE EXPLAIN PLAN> LOGS: 把执行计划写入日志,定期审计哪些查询经常触发全扫。话说回来,
三、如何让你的查询“爱上”Index?—实战调整建议
- Add right index first:
- - 单列唯一/非唯一 Index 对应最常用过滤字段;
- - 多列组合 Index 顺序尽量符合 WHERE + ORDER BY 的排列;
- - 用虚拟/计算列做 “存储” 后再建立 Index;
- - 如需 substring,可预先生成派生字段 `prefix_3 CHAR` 并建立 Index。
- - 将 OR 拆成 UNION ALL 并各自加单独 Index;
- - 对于频繁组合,可考虑多层 Covering Index 包含所有返回字段。
- - 避免以通配符开头,如 `%abc%`;
- - 若必须模糊搜索,可启用全文检索 Engine 或 trigram 索引用例。
-
- 定期 `ANALYZE TABLE` / `UPDATE STATISTICS`;- 对碎片严重的大表 `REBUILD INDEX WITH `。- 在数据插入高峰后立,也就是刷新,以免老计划影响新事务。.
-
- 确保热点值占比低于5%;其实,如热点>20%,可考虑将热点值单独存放或做缓存。
-
- PostgreSQL 的 partial index 可只针对满足特定条件的数据行建立,例如 `
四、案例速递—从慢到快的一次完整改造流程
-- 原始慢查询
SELECT * FROM orders
WHERE DATE = '2026‑08‑01'
AND SUBSTRING = 'John';-- 步骤一:添加派生列
ALTER TABLE orders ADD COLUMN name_prefix CHAR;UPDATE orders SET name_prefix = LEFT;按理说,-- 步骤二:创建覆盖性复合 Index
CREATE INDEX idx_orders_date_nameprefix ON orders;-- 步骤三:
查询
SELECT * FROM orders
WHERE DATE = '2026‑08‑01'
AND name_prefix = 'John';老实说,-- 步骤四:分析执行计划
EXPLAIN SELECT * FROM orders WHERE DATE='2026‑08‑01' AND name_prefix='John';话说回来,>> 必须看到 USING INDEX
>> rows examined ≈ rows returned + small overhead;
要点这方面。
* 本内容基于 MySQL / PostgreSQL / Oracle 等主流 RDBMS 的共通经验整理,请结合实际版本差异调整细节。.
这篇文章共计约2300字,预计阅读时间约8–12分钟。本内容已由专业 DBA 审核并,请按需引用与部署!..
在实际项目中。许多开发者都会遇到“为什么我的查询没有走索引?”这类问题,说起来,往往因为一次慢查询导致整个应用响应变慢、数据库连接数飙升甚至服务宕机。
一、常见导致不走索引的原因
-
查询条件不包含索引列
如果WHERE子句里根本没有用到被建好的列,数据库自然不会去利用它们。
💡 痛点:你经常看到慢日志里写着“Table scan”,却忘记检查是否遗漏了关键字段。老实说,
-
对索引列使用函数或表达式
例如
WHERE SUBSTRING='A娱乐'或WHERE age+1=30。数据库需要先对每行执行该函数。再比较结果,无法直接定位。
💡 痛点:业务需求变更后把业务字段包装成了表达式,却不知道这会让查询失效。
-
数据量过小
当表只有几百条记录时全表扫描的成本比走索引更低。
💡 痛点:你为一个极小的数据集加了复杂的复合索引,却发现反而更慢。
-
数据分布不均匀
某些值出现频率极高,导致扫描大量行。
💡 痛点:业务日志表里某个状态字段几乎全是同一个值,任何基于该字段的查询都快要变成扫表。
-
统计信息过时或失效
DROP INDEX / CREATE INDEX或者执行
MIGRATE STATISTICS / ANALYZE TABLE数据库根据统计决定是否使用索引;如果统计错误就会误判为全扫。
💡 痛点:最近一次大批量插入后没有刷新统计,导致旧计划被缓存继续走错误方法。
-
强制不使用索引的提示或语法(如
NOSORT/MATCHEDONLY/FORCESEEK) -
MULTI‑OR 条件无效化
`WHERE a=1 OR b=2` 如果a和b分别都有独立有效的单列索引。但调整器认为两者组合成本高,就会退回全扫。
- LIKE 开头通配符导致无法利用前缀匹配** *
- IDLE 或破损的物理结构** *
二、使用者痛点拆解与排查步骤
- 1️⃣ 查询慢但未显式报错: SQL执行时间>5s → 看EXPLAIN Plan,看是否有Index Used标记;若无则说明走了全表扫描,
- 2️⃣ 高并发下CPU/IO飙升: 监控CPU & I/O → 排查是否有大量重复扫描同一大表;若是则需考虑添加覆盖/聚簇等优先级更高的Index。
- 3️⃣ 长事务占用锁资源: 长时间未提交的 SELECT 在某些DBMS上会升级为共享锁;若是大范围扫描,会阻塞写操作。
再看排查技巧,
- COLUMN IS NULL CHECK: 在调试之前确认是否有NULL值被忽略——NULL通常不会被普通B‑Tree Index覆盖。
- AUTO ANALYZE 设置: 开启自动统计更新,让DBMS自行维护最新分布信息。
- EVALUATE EXPLAIN PLAN> LOGS: 把执行计划写入日志,定期审计哪些查询经常触发全扫。话说回来,
三、如何让你的查询“爱上”Index?—实战调整建议
- Add right index first:
- - 单列唯一/非唯一 Index 对应最常用过滤字段;
- - 多列组合 Index 顺序尽量符合 WHERE + ORDER BY 的排列;
- - 用虚拟/计算列做 “存储” 后再建立 Index;
- - 如需 substring,可预先生成派生字段 `prefix_3 CHAR` 并建立 Index。
- - 将 OR 拆成 UNION ALL 并各自加单独 Index;
- - 对于频繁组合,可考虑多层 Covering Index 包含所有返回字段。
- - 避免以通配符开头,如 `%abc%`;
- - 若必须模糊搜索,可启用全文检索 Engine 或 trigram 索引用例。
-
- 定期 `ANALYZE TABLE` / `UPDATE STATISTICS`;- 对碎片严重的大表 `REBUILD INDEX WITH `。- 在数据插入高峰后立,也就是刷新,以免老计划影响新事务。.
-
- 确保热点值占比低于5%;其实,如热点>20%,可考虑将热点值单独存放或做缓存。
-
- PostgreSQL 的 partial index 可只针对满足特定条件的数据行建立,例如 `
四、案例速递—从慢到快的一次完整改造流程
-- 原始慢查询
SELECT * FROM orders
WHERE DATE = '2026‑08‑01'
AND SUBSTRING = 'John';-- 步骤一:添加派生列
ALTER TABLE orders ADD COLUMN name_prefix CHAR;UPDATE orders SET name_prefix = LEFT;按理说,-- 步骤二:创建覆盖性复合 Index
CREATE INDEX idx_orders_date_nameprefix ON orders;-- 步骤三:
查询
SELECT * FROM orders
WHERE DATE = '2026‑08‑01'
AND name_prefix = 'John';老实说,-- 步骤四:分析执行计划
EXPLAIN SELECT * FROM orders WHERE DATE='2026‑08‑01' AND name_prefix='John';话说回来,>> 必须看到 USING INDEX
>> rows examined ≈ rows returned + small overhead;

