MySQL 直方图和索引基数如何影响优化器选择?
简化版
索引基数表示列中不同值的大致数量,基数越高,索引区分度通常越好。优化器会根据统计信息估算扫描行数和成本,再决定是否走索引、走哪个索引。直方图可以补充无索引列或数据分布不均列的统计信息,让优化器更准确理解某些值很常见、某些值很少见。统计信息过旧或分布倾斜时,优化器可能选错索引。
详细版
MySQL 优化器不是直接“知道”真实数据量,而是基于统计信息估算成本。比如 status 列只有几个值,单独索引区分度很低;而 user_id 列值很多,通常更适合过滤。问题在于真实业务数据常常倾斜,例如 status='success' 占 99%,status='failed' 占 1%,如果统计信息不准确,执行计划就可能不稳定。
直方图的作用是描述列值分布,帮助优化器做更接近真实的选择。它不是索引,不能直接加速读取,但可能让优化器少选错计划。
完整版教学
一、索引基数是什么
基数可以理解为列中不同值数量。
不同值越多,过滤能力通常越强。
性别、状态这类列基数低。
手机号、用户 id 这类列基数高。
基数是优化器估算选择率的重要依据。
二、为什么优化器会选错
优化器依赖统计信息。
统计信息可能过期。
数据分布可能严重倾斜。
查询条件组合可能和单列统计不一致。
这些都会导致估算行数偏离真实行数。
三、直方图解决什么
直方图描述列值分布。
它能让优化器知道某些值很多,某些值很少。
尤其适合没有索引但常参与过滤的列。
也适合低基数但分布极不均匀的列。
直方图不是索引,它不负责定位数据,只负责帮助优化器估算成本。
四、示例场景
SELECT *
FROM orders
WHERE status = 'failed'
AND created_at >= '2026-07-01';
如果 failed 很少,走状态相关过滤可能更有利。
如果优化器误以为各状态平均分布,就可能选择不理想计划。
直方图能让估算更接近真实分布。
五、优化手段对比
| 手段 | 作用 | 注意点 |
|---|---|---|
| 更新统计信息 | 刷新表和索引统计 | 解决过期问题 |
| 直方图 | 描述列值分布 | 不等于索引 |
| 联合索引 | 提供访问路径 | 有写入和空间成本 |
| Hint | 人工干预计划 | 容易绑定当前数据分布 |
六、什么时候不该依赖直方图
如果查询本身需要大量回表,直方图不能消除 IO。
如果条件列非常高频过滤,索引可能更合适。
如果业务数据变化很快,直方图也可能过期。
如果 SQL 写法导致索引失效,统计信息再准也救不了。
七、误区和追问
- 误区:基数高的索引一定会被选中。 优化器还会考虑范围、排序、回表、覆盖和成本。
- 误区:直方图能替代索引。 直方图只改善估算,不提供数据访问路径。
- 误区:优化器选错一定是 Bug。 很多时候是统计信息旧、数据倾斜或 SQL 条件复杂。
- 追问:如何判断估算偏差? 对比
EXPLAIN估算行数和实际执行扫描行数。 - 追问:低基数字段要不要建索引? 要看分布、查询条件和组合索引设计,不能只看基数低就否定。
- 追问:直方图适合哪些列? 常用于过滤、分布倾斜、未建索引但影响计划的列。
八、面试收束
回答时先讲优化器依赖统计信息。
再解释基数、数据倾斜和直方图。
最后补充它和索引、Hint、统计刷新之间的边界。