MySQL多列索引缺失最左前缀条件时的查询行为判定
题目场景
- 已知某张表仅存在一个二级索引:
idx_goods (is_deleted, price) - 索引最左列
is_deleted的取值只有0、1两种 - 待执行查询的过滤条件为:
where price < 10,请从以下描述中选出该查询对应的真实执行行为:- MySQL对二级索引做全量扫描以匹配price条件
- MySQL对二级索引做部分扫描以匹配price条件:从二级索引中
is_deleted=0的位置开始扫描,到达price=10的位置后,跳转到二级索引中is_deleted=1的位置继续扫描 - MySQL忽略该二级索引,扫描基表以匹配price条件,换言之:若查询条件未覆盖索引键的最左前缀,即使条件字段属于索引键的组成部分,也不会在二级索引上匹配该条件,而是在基表上完成匹配
正确答案
选项1
原理解释
- 联合索引
idx_goods (is_deleted, price)按B+树结构组织,排序规则非常明确:先按is_deleted列的值升序排列,is_deleted值相同的记录,再按price列的值升序排列。整个索引的叶子节点是串在一起的有序链表,顺序是所有is_deleted=0的记录按price从小到大排完,再接上所有is_deleted=1的记录按price从小到大排。 - 选项3是非常普遍的认知误区:很多人觉得没带上索引最左列的条件,索引就完全失效只能扫基表,这个理解是错的。最左前缀原则的核心限制是:没有最左列条件时,无法通过B+树直接定位到非最左列符合条件的起始位置,没法直接做范围剪枝,但不代表完全不能使用二级索引。二级索引的叶子节点本身就存储了
is_deleted、price和对应的主键值,扫描索引的时候当场就能判断price < 10的条件,根本不需要回基表再做匹配。如果查询要返回的字段全部包含在二级索引里(也就是覆盖索引场景),甚至不需要访问基表就能直接返回结果,不存在“必须在基表匹配条件”的硬性规则。只有当优化器计算成本后,认为全量扫二级索引+回表取字段的开销比直接全表扫更高时,才会选择扫描基表,这是基于成本的选择,不是最左前缀原则的强制要求。 - 选项2描述的是MySQL 8.0.13版本后引入的索引跳跃扫描(Index Skip Scan)的执行逻辑:当最左索引列的基数极低(比如本题只有0、1两个值)时,优化器会枚举最左列的所有唯一值,拼接成多个带最左列等值条件+后续列条件的范围查询,分别走索引范围扫描,跳过大量不符合条件的索引页减少扫描量。但这个优化有严格的触发限制(比如要求覆盖索引、统计信息准确等),不是所有场景下的默认行为,题目没有指定特殊版本、参数的前提下,不属于通用执行逻辑。
- 在未触发索引跳跃扫描的常规场景下,MySQL不会自动拆分最左列的枚举值做剪枝,只能从二级索引的第一个叶子节点开始,顺着有序链表把整个二级索引从头到尾扫一遍,逐行判断
price < 10的条件,符合条件的记录再根据查询需求决定是否回表获取其他字段,也就是选项1描述的全量扫描二级索引匹配条件的行为。
内容的提问来源于stack exchange,提问作者abracadabra
相关产品推荐
相关产品推荐

