一张表 没有走索引扫描,原因有哪些(回表成本)
数据库不走索引扫描的主要原因包括1索引失效2数据量过少3WHERE条件过滤后数据量仍很大。当过滤后保留大量数据如10亿条中保留9.9亿条时回表操作索引查找数据读取的成本会高于全表扫描。数据库优化器会计算索引成本和全表扫描成本选择更低的方式。其他影响因素还包括函数使用、统计信息过时等。验证方法可通过执行计划查看但强制使用索引通常不推荐。核心原则是索引并非万能数据量大时全表扫描可能更高效。一张表 没有走索引扫描原因有哪些Q一张表 没有走索引扫描,原因有哪些A索引失效了;数据量太少了,系统不走索引;WHERE 过滤完之后数据量依旧非常大,对比过滤前没什么区别;比如原始表有 10 亿条数据 你 WHERE 筛选完了还有 9.9亿条怎么理解第3点的深入理解核心逻辑回表成本 vs 全表扫描成本当你通过索引查询时数据库需要做两件事索引扫描在索引树中找到符合条件的记录位置回表根据索引中的ROWID去数据表中读取完整数据为什么数据量太大时不走索引场景举例sql-- 假设表有10亿条数据其中9.9亿条满足 status ACTIVE SELECT * FROM users WHERE status ACTIVE;走索引的成本估算索引扫描快速找到9.9亿个ROWID快回表9.9亿次随机I/O去读取完整行极慢总成本 ≈ 9.9亿次随机I/O全表扫描的成本估算顺序读取整个表的10亿条数据连续I/O很快利用多块读一次读多个数据块总成本 ≈ 连续读取所有数据块结果全表扫描反而比索引回表更快形象的比喻假设有一本10万页的电话簿10万条记录场景1找特定的人走索引你有姓名索引字母顺序表先查索引找到页码快翻到那一页1次随机翻页✅ 效率极高场景2找所有住在北京的人占90%走索引方式索引告诉你每个北京人所在的页码 → 翻9万次随机页慢死全表扫描从第1页翻到第10万页连续翻页反而更快数据库的优化器决策逻辑sql-- 数据库内部估算公式简化版 索引成本 索引扫描成本 回表成本 × 预估行数 全表扫描成本 表的总块数 / 多块读系数 -- 选择成本更低的方式 IF 索引成本 全表扫描成本 THEN 使用索引 ELSE 全表扫描实际例子sql-- 表员工表 100万行 -- 索引dept_id索引 -- 情况1过滤后数据少 → 走索引 SELECT * FROM emp WHERE dept_id 99; -- 只有10条 -- 回表10次很快 ✓ 走索引 -- 情况2过滤后数据多 → 不走索引 SELECT * FROM emp WHERE dept_id IN (1,2,3); -- 80万条 -- 回表80万次太慢 ✗ 全表扫描 -- 情况3查询所有列 vs 只查索引列 SELECT emp_name FROM emp WHERE status ACTIVE; -- 9.9亿条 -- 如果emp_name在索引中覆盖索引不需要回表 → 可能走索引补充其他不走索引的原因除了你说的3点还有使用了函数或类型转换sqlWHERE YEAR(create_date) 2024 -- 函数破坏索引 WHERE id 123 -- 隐式类型转换id是数字使用不等于、LIKE %xxxsqlWHERE status ! DELETED -- 不等于 WHERE name LIKE %张三% -- 以通配符开头统计信息过旧表数据变化了但统计信息没更新优化器误判选了错误的执行计划NULL值问题sqlWHERE name IS NOT NULL -- 复合索引可能失效如何验证sql-- 查看执行计划看是否走了索引 EXPLAIN PLAN FOR SELECT * FROM emp WHERE dept_id 10; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- 强制走索引不推荐只用于测试 SELECT /* INDEX(emp idx_dept_id) */ * FROM emp WHERE dept_id IN (1,2,3);总结索引不是万能的当回表成本太高时全表扫描反而是更优选择。数据库优化器会根据数据分布自动选择最优路径。正确的理解应该是原始表有 10 亿条数据WHERE 筛选后还剩 9.9 亿条这句话的意思是WHERE 条件过滤掉了0.1 亿条1000万条WHERE 条件保留了9.9 亿条也就是说要查询的数据量非常大占总数据的99%更清晰的表述方式应该这样说WHERE 条件过滤掉的数据很少需要返回的数据量依然很大占总数据的绝大部分举例说明sql-- 表10亿条订单记录 -- 你想查除了已删除之外的所有订单 -- 情况A过滤掉很少 SELECT * FROM orders WHERE status ! DELETED; -- 假设只有100万条是DELETED剩下9.9亿条需要返回 -- 结果不走索引因为要返回99%的数据 -- 情况B过滤掉很多 SELECT * FROM orders WHERE status DELETED; -- 只返回100万条占总数据1% -- 结果走索引因为返回数据量小为什么这样就不走索引text走索引的成本 索引查找成本 回表成本 × 9.9亿次 全表扫描成本 顺序读取10亿条的成本 9.9亿次随机回表 顺序读取10亿条 所以数据库选择全表扫描你原先批注的那句话你写的3,WHERE 过滤完之后数据量依旧非常大,对比过滤前没什么区别;这个理解是完全正确的就是说过滤前10亿过滤后9.9亿过滤比例很小只减少了1%所以和没过滤差不多走索引没意义正确的说法对比正确说法筛选掉了小部分过滤后剩余大量WHERE保留了9.9亿只删了0.1亿总结当WHERE条件过滤效率低过滤掉的数据很少保留的数据很多时数据库不会走索引因为回表成本太高不如直接全表扫描。