(b,a)索引:Select * from t where a=1:为何Explain后type为Index,而非All?

一、背景知识:什么是联合索引?

在数据库(如 MySQL)中,联合索引(Composite Index)是由多个列组成的索引,例如 KEY idx_b_a (b, a) 表示一个由列 b 和列 a 按顺序组成的索引。联合索引的底层通常基于 B+ 树,其键值按照索引定义的顺序(先 b,后 a)排序。

联合索引的特性
  1. 左前缀原则 :查询条件必须从索引的最左列开始利用,否则无法命中索引。例如:
    • WHERE b = 1 AND a = 123 能用上 (b, a)
    • WHERE a = 123 只能部分利用或完全不利用,取决于优化器。
  2. 存储结构 :B+ 树叶子节点按 (b, a) 顺序存储,包含索引列和主键值(对于 InnoDB)。

现在,我们来分析 SELECT * FROM table WHERE a = 123 在索引 (b, a) 下的执行过程。


二、查询分析:SELECT * FROM table WHERE a = 123

1. 查询条件与索引匹配
  • 查询条件只有 a = 123,而索引是 (b, a)
  • 根据左前缀原则,索引的最左列 b 未出现在 WHERE 条件中,因此无法直接通过 b 的值定位到具体的索引范围。
  • 但是,a 是联合索引的一部分,优化器可能会选择利用索引 (b, a),而不是完全忽略它。
2. 是否走全索引扫描?

答案是:可能会走全索引扫描(Index Scan),但不一定是全表扫描(Table Scan),具体取决于优化器的选择。

  • 执行过程
    1. MySQL 优化器发现 a(b, a) 的一部分,虽然 b 未指定,但索引中每一行都包含 a 的值。
    2. 优化器可以选择扫描整个 (b, a) 索引,逐行检查 a 是否等于 123。
    3. 对于符合条件的记录,通过索引中的主键值回表(若需要 * 中的非索引列)。
  • 为什么不是全表扫描?
    • 如果表有主键索引和 (b, a) 索引,扫描 (b, a) 索引通常比扫描整个表(全表扫描)更高效,因为索引的数据量通常小于表数据,且 B+ 树的顺序访问性能较高。
  • 是否高效?
    • 不高效。因为没有 b 的条件,相当于遍历整个索引,性能接近于全表扫描,尤其当表数据量很大时。
3. 可能的优化器行为
  • 如果表数据量小,或 (b, a) 是覆盖索引(即 SELECT 只涉及 ba,无需回表),优化器可能倾向于用索引。
  • 如果表很大且选择性低(a = 123 匹配大量行),优化器可能直接选择全表扫描。

三、使用 EXPLAIN 查看执行计划

我们可以用 EXPLAIN 查看 MySQL 的执行计划,以下是分析结果的可能输出和解释:

示例 SQL
sql 复制代码
EXPLAIN SELECT * FROM table WHERE a = 123;
可能的输出
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE table index idx_b_a idx_b_a 8 NULL 1000 Using where
字段解析
  1. typeindex
    • 表示全索引扫描(Index Scan),扫描整个 (b, a) 索引。
    • 原因:条件 a = 123 无法利用索引的最左列 b,但仍使用了索引。
  2. possible_keysidx_b_a
    • 表示优化器考虑使用的索引。
  3. keyidx_b_a
    • 实际使用的索引。
  4. rows1000
    • 估计扫描的行数(整个索引的行数)。
  5. ExtraUsing where
    • 表示在扫描索引后,用 WHERE 条件过滤出 a = 123 的行。
结论
  • 这里是全索引扫描,而不是范围扫描(range)或唯一查找(ref),因为 b 未受限。

四、回顾 EXPLAIN 中的常见 type 类型

EXPLAINtype 列反映了查询的访问类型,从高效到低效排序如下:

  1. const

    • 通过主键或唯一索引精确匹配一行(如 WHERE id = 1)。
    • 效率最高。
  2. eq_ref

    • 在联表查询中,通过主键或唯一索引匹配一行。
    • 示例:SELECT * FROM t1 JOIN t2 ON t1.id = t2.id
  3. ref

    • 通过非唯一索引查找匹配的行,可能返回多行。
    • 示例:WHERE a = 123a 有普通索引)。
  4. range

    • 通过索引进行范围扫描。
    • 示例:WHERE a > 100 AND a < 200
  5. index

    • 全索引扫描,扫描整个索引树。
    • 示例:本文的 WHERE a = 123(b, a)
  6. ALL

    • 全表扫描,逐行检查表数据。
    • 示例:无索引可用时。
记忆技巧
  • const/ref:精确查找,效率高。
  • range:范围查找,部分索引。
  • index/ALL :全扫描,index 用索引,ALL 用表,效率较低。

五、总结与优化建议

  1. 查询分析结论

    • 对于 SELECT * FROM table WHERE a = 123,索引 (b, a) 会导致全索引扫描(type = index),因为无法利用最左列 b 缩小范围。
    • 如果数据量大,性能接近全表扫描。
  2. 优化建议

    • 如果 a 的查询频率高,考虑单独为 a 创建索引(如 KEY idx_a (a)),这样查询会走 refrange
    • 如果业务允许,调整查询加上 b 的条件(如 WHERE b = xxx AND a = 123),充分利用 (b, a)
相关推荐
neo_Ggx2312 分钟前
Spring上下文工具类
java·后端·spring
西岭千秋雪_41 分钟前
Spring MVC源码分析の请求处理流程
java·后端·spring·mvc·springboot
web147862107231 小时前
Spring Framework 中文官方文档
java·后端·spring
Pitayafruit1 小时前
【📕分布式锁通关指南 07】源码剖析redisson利用看门狗机制异步维持客户端锁
redis·分布式·后端
小菜不菜_xc1 小时前
Spring Boot + MyBatis-Plus 最全配置指南,让你的项目更高效!
java·后端·spring
uhakadotcom1 小时前
阿里云PAI:一站式机器学习平台
后端·面试·github
Matrix701 小时前
Scala编程_数组、列表、元组、集合与映射
开发语言·后端·scala
uhakadotcom1 小时前
阿里云可观测监控Prometheus版:简化监控,提升效率
后端·面试·github
37手游后端团队1 小时前
聊聊提示词注入攻击那些事
人工智能·后端·程序员
Asthenia04121 小时前
从实习生“777惨案”聊起:上生产环境部署 Spring Boot Docker 的正确姿势
后端