为何MySQL执行SELECT *时不使用新建的otherId索引?
问题原因分析
MySQL选择全表扫描而非使用otherId唯一索引,核心原因是优化器基于成本计算,判定全表扫描的开销低于「索引查找+回表」的组合操作,具体拆解如下:
1. 覆盖索引与回表的差异
- 当执行
SELECT id, otherId FROM Entity WHERE otherId = ?时,Entity_otherId_key唯一索引本身已经包含otherId(索引列)和id(InnoDB二级索引会自动附加主键列),无需访问聚簇索引(主键索引)即可返回所有需要的数据,触发覆盖索引(对应EXPLAIN结果中的Using index),成本极低,因此优化器优先选择索引。 - 当执行
SELECT *时,需要获取表中所有列的数据。此时通过Entity_otherId_key找到目标行的主键后,必须再去聚簇索引中读取完整的行数据,这个过程称为回表。回表会额外增加IO开销,优化器会评估这部分开销是否值得。
2. 小表的成本评估逻辑
你的表总行数仅1187行,属于典型的小表。InnoDB在处理小表时,全表扫描可以一次性读取多个连续的数据页,缓存命中率高,实际执行的IO开销非常低。相比之下,使用二级索引需要先遍历索引页找到主键,再跳转至聚簇索引页读取数据,两次IO操作的总开销,被优化器判定为高于直接全表扫描。
3. 选择性的间接影响
虽然otherId是唯一索引,但非空值仅占总行数的14.4%(171/1187)。对于小表来说,即使单条查询仅匹配1行数据,优化器依然会认为:扫描全部1187行的成本,比「索引定位+回表」的两步操作更划算——尤其是当表数据已经在内存缓存中的时候,全表扫描的速度几乎和索引查找无差异。
验证方法
你可以通过强制使用索引来对比性能:
EXPLAIN SELECT * FROM Entity FORCE INDEX (Entity_otherId_key) WHERE Entity.otherId = ?;
实际执行后会发现,强制索引的执行时间和全表扫描差异极小,甚至全表扫描更快,这也印证了优化器的选择逻辑。
内容的提问来源于stack exchange,提问作者Simon Knittel
相关产品推荐
相关产品推荐

