(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 只涉及 b 和 a,无需回表),优化器可能倾向于用索引。
  • 如果表很大且选择性低(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. type :index
    • 表示全索引扫描(Index Scan),扫描整个 (b, a) 索引。
    • 原因:条件 a = 123 无法利用索引的最左列 b,但仍使用了索引。
  2. possible_keys :idx_b_a
    • 表示优化器考虑使用的索引。
  3. key :idx_b_a
    • 实际使用的索引。
  4. rows :1000
    • 估计扫描的行数(整个索引的行数)。
  5. Extra :Using where
    • 表示在扫描索引后,用 WHERE 条件过滤出 a = 123 的行。
结论
  • 这里是全索引扫描,而不是范围扫描(range)或唯一查找(ref),因为 b 未受限。

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

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

  1. const

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

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

    • 通过非唯一索引查找匹配的行,可能返回多行。
    • 示例:WHERE a = 123(a 有普通索引)。
  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)),这样查询会走 ref 或 range。
    • 如果业务允许,调整查询加上 b 的条件(如 WHERE b = xxx AND a = 123),充分利用 (b, a)。
相关推荐
专业程序开发源2 小时前
django新闻推荐系统70655-计算机课程设计、毕业设计
java·javascript·spring boot·后端·python·django·课程设计
打工仔折腾 AI2 小时前
把 AI Agent 托管到家里电脑:UU远程端口映射与CLI实测记录
人工智能·后端·python·langchain·电脑·ai agent 实战
vx_Biye_Design2 小时前
springboot一站式旅游管理平台81037-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·课程设计·express·旅游
嵌入式学习菌2 小时前
ESP32 ModbusTCP 分片缓存
java·后端·spring
程序员小杰@2 小时前
Spring Boot 常用注解分类速记
java·spring boot·后端
挖掘狂人2 小时前
Git 从 0 到 1:用一个小项目走完 add / commit / reset / merge / rebase / push
git·后端·github
vx_Biye_Design3 小时前
springboot小学生英语学习APP62773-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·学习·课程设计
打工仔折腾 AI3 小时前
用UU远程把家里电脑变成AI Agent常驻服务器:CLI、端口映射与代理实测
运维·服务器·人工智能·后端·python·电脑·ai agent 实战
明月_清风3 小时前
AI Agent 进入系统层:现在有哪些开源项目在解决这个问题?
人工智能·后端
弈栈录3 小时前
MySQL 事务、索引与锁:后端开发必须掌握的数据库基础
数据库·后端