mysql为何执行计划总是走全表扫描_分析优化器成本计算逻辑

2026-04-09数据库243118

EXPLAIN显示type=ALL但有可用索引,是因为优化器基于成本估算选择全表扫描更优;常见原因包括高匹配行数、低区分度索引、大回表开销及过期统计信息。

为什么 EXPLAIN 显示 type=ALL 却有可用索引?

MySQL 优化器不是“看到索引就用”,而是基于成本估算选它认为最省的路径。即使 WHERE 条件字段上有索引,只要优化器预估走索引+回表的总成本 > 全表扫描,就会选 ALL

常见诱因包括:

  • WHERE 条件匹配行数太多(比如 status != 'done' 在 95% 行都满足时)
  • 索引区分度低(如 gender 只有 'M'/'F' 两值)
  • 查询返回大量列,导致回表开销远超预期
  • 统计信息过期(INFORMATION_SCHEMA.STATISTICS 中的 cardinality 不准)

怎么查清优化器到底怎么算的成本?

MySQL 8.0+ 支持 optimizer_trace,能直接看到成本计算过程,比猜靠谱得多。

实操步骤:

  • 执行 SET optimizer_trace="enabled=on";
  • 运行你的慢查询
  • SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
  • 重点看 steps 数组里 considered_execution_planscost_for_plan

你会发现:哪怕索引扫描的 read_cost 很低,但加上 eval_cost(过滤行数 × 每行评估开销)和 prefix_cost(连接开销)后,总成本反而更高。

FORCE INDEX 强制走索引一定更优吗?

不一定。强制索引只是跳过成本估算,不改变实际执行代价。在某些场景下反而更慢。

疯狂翻译师App

支持屏幕、图片、视频字幕、文档、漫画等多种翻译,准确率高,操作简单。

下载

典型翻车现场:

  • 小表(
  • 覆盖索引没建好,强制后要回主键二次查找,而全表扫描正好是顺序读
  • 索引本身碎片严重(DATA_FREE 大、页利用率低),物理读放大

验证方法:用 BENCHMARK()SYS.STATISTICS 对比两种执行的实际 Handler_read_* 计数,别只看 EXPLAINrows

哪些操作真正影响优化器的成本判断?

不是所有“加索引”都能被成本模型正确认知。以下操作直接影响其输入参数:

  • 执行 ANALYZE TABLE —— 更新索引基数(cardinality),尤其对大表增删频繁后必须做
  • 调整 innodb_stats_persistent_sample_pages(默认 20)—— 样本页越多,基数越准,但 ANALYZE 更慢
  • 避免在 WHERE 中对索引列用函数:WHERE YEAR(created_at) = 2023 会失效索引,优化器只能退化为全表
  • 联合索引顺序必须匹配查询模式:如果经常查 WHERE a=1 AND b>10(a,b) 有效,(b,a) 就只能用上 a 的等值部分

成本模型本身不理解业务语义,它只认统计数字和规则。索引设计、数据分布、配置参数三者稍有偏差,type=ALL 就可能反复出现 —— 这不是 bug,是它在按规则算账。

本文地址:https://www.ufcn.cn/article/2304493.html

如非特殊说明,本站内容均来自于网友自主分享,概不代表本站观点,如有任何问题我们都将在收到反馈后的第一时间进行处理!