为何带主键索引的SELECT * FROM t_user WHERE id >=10000 LIMIT 5会扫描大量行?
针对你遇到的select * from t_user where id >=10000 limit 5;执行计划扫描近500万行的问题,核心原因主要有以下几点:
统计信息过期或不准确
MySQL优化器依赖表的统计信息判断索引使用效率。如果t_user表近期有大量数据变更(比如批量插入、删除)但统计信息未及时更新,优化器可能错误评估id >=10000的结果集规模,选择低效执行路径,导致估算的扫描行数远高于实际。可执行ANALYZE TABLE t_user;更新统计信息后重新查看执行计划。聚簇索引存在碎片或空洞
InnoDB主键是聚簇索引,数据按主键顺序存储。若曾经批量删除过id <10000的行,或存在大量不连续主键插入操作,会导致聚簇索引出现大量空洞。MySQL定位id >=10000的起始位置时,需遍历这些空洞对应的索引页,执行计划会将这些遍历的页行数计入扫描行数。可通过OPTIMIZE TABLE t_user;整理索引碎片、修复空洞问题。执行计划的扫描行数是估算值
执行计划显示的扫描行数是优化器基于统计信息的估算值,并非实际执行时的真实扫描行数。当数据分布不均匀时,估算值与真实值会存在较大偏差。若使用MySQL 8.0及以上版本,可通过EXPLAIN ANALYZE语句查看真实执行行数,对比估算值是否准确。优化器的选择偏差
极端情况下,优化器可能错误判断id >=10000覆盖了绝大多数数据,认为全表扫描比使用主键索引更高效,从而放弃使用主键索引。这种情况可通过FORCE INDEX(PRIMARY)强制指定使用主键索引,验证执行计划是否改变:select * from t_user FORCE INDEX(PRIMARY) where id >=10000 limit 5;
内容的提问来源于stack exchange,提问作者yates

