bigywater.tech
MySQL / InnoDB · 13

选择性与索引扫描:优化器如何决定走不走索引

建一张20万行、95%对5%严重倾斜的表,用FORCE/IGNORE INDEX对比走索引和全表扫描的耗时,意外发现MySQL优化器对低选择性列的默认选择其实选错了。

2.6倍
低选择性时走索引比全表扫描慢的倍数
3.8倍
高选择性时走索引比全表扫描快的倍数
估算偏差近1倍
优化器对倾斜数据的行数估算误差

做的事:一张20万行、95%对5%严重倾斜的表

status列只有两个值,active占95%,banned占5%,status列建了索引。

建表插入CREATE TABLE t3b (id INT PRIMARY KEY AUTO_INCREMENT, status VARCHAR(20), payload VARCHAR(200), INDEX idx_status(status)); 插入20万行,95%概率status='active',5%概率status='banned' SELECT status, COUNT(*) FROM t3b GROUP BY status → active 189960行(约95%) → banned 10040行(约5%)

"选择性"指的是:一个条件能筛掉多大比例的数据。status='banned' 选择性很高(只留5%),status='active' 选择性很低(留下95%,几乎没筛掉什么)。理论上优化器应该对低选择性的条件更倾向于全表扫描,因为走索引再逐条回表,可能还不如直接顺序扫全表划算。

验证:强制索引 vs 强制全表扫描,实际耗时对比

用 FORCE INDEX 和 IGNORE INDEX 控制走不走索引,用EXPLAIN ANALYZE测耗时。

结果(status='active',95%的行,低选择性)IGNORE INDEX(强制全表扫描)→ 耗时 52.2 ms FORCE INDEX(强制走索引) → 耗时 134 ms (比全表扫描慢约2.6倍!) 结果(status='banned',5%的行,高选择性)IGNORE INDEX(强制全表扫描)→ 耗时 34.2 ms FORCE INDEX(强制走索引) → 耗时 9.04 ms (比全表扫描快约3.8倍)

结果符合直觉:条件选择性高(banned,5%)时走索引明显更快;条件选择性低(active,95%)时走索引反而更慢,因为要对19万行逐个回表,还不如直接顺序扫描20万行来得干脆。

意外发现:优化器默认选择选错了

不加任何强制提示,MySQL自己会怎么选?

结果,不加任何FORCE/IGNORE提示EXPLAIN SELECT * FROM t3b WHERE status='active' → Index lookup on t3b using idx_status (status = 'active') (cost=11736 rows=98851) EXPLAIN ANALYZE SELECT * FROM t3b WHERE status='active' → 耗时 131 ms(跟FORCE INDEX的134ms几乎一样——优化器选择了走索引) EXPLAIN SELECT * FROM t3b WHERE status='banned' → Index lookup on t3b using idx_status (status = 'banned') (cost=3673 rows=18222)(优化器正确选择走索引)

结果:对于95%都命中的 active,MySQL优化器默认选择走索引,耗时131ms——比强制全表扫描的52.2ms反而慢了2.5倍!查EXPLAIN里的估算行数能看出原因:优化器对 active 的行数估算是 98851,只有表的一半左右,跟的189960(95%)差了将近一倍。

原因是 MySQL 的索引统计信息是抽样估算的,不是精确统计。数据分布越倾斜(这里是95%对5%的极端倾斜),抽样估算的误差就可能越大,优化器基于错误的行数估算做出的判断也就可能是错的。这也是为什么场景里遇到"明明有索引,查询却很慢"时,第一步该做的不是加更多索引,而是先跑一次 ANALYZE TABLE 更新统计信息,再用EXPLAIN看看优化器的行数估算是否靠谱。

分步演示:选择性和优化器的判断偏差

每一步都是本文上面用EXPLAIN ANALYZE跑出来的原始耗时。

选择性决定走索引划算不划算,但优化器的估算可能出错
这一步新验证的场景

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),20万行、95%对5%倾斜分布的表
    • FORCE INDEX / IGNORE INDEX:控制走不走索引
    • EXPLAIN ANALYZE:执行并测量实际耗时
    京ICP备2026067502号-1京公网安备11010502063487号