索引的排序和分组

先把前提说清楚:

表有两个索引:

  • 联合索引:idx_abcd (a, b, c)
  • 单列索引:idx_c (c)

你问的两条 SQL:

  1. WHERE b = 1 ORDER BY c不走索引,全表扫描 + filesort
  2. WHERE b = 1 GROUP BY c能用上 idx_c(c 的单列索引)

下面讲透为什么不一样(完全从优化器成本 + 索引结构角度讲)。


一、先说:两条 SQL 都用不了联合索引 (a,b,c)

联合索引要满足最左前缀 :必须从 a 开始,连续向右匹配。

  • 你的条件只有 b=1没有 a
  • 所以:(a,b,c) 直接废掉,两条语句都用不了它

现在只剩一个候选索引:idx_c (c)


二、为什么 WHERE b=1 ORDER BY c 不用 idx_c

语句:

sql 复制代码
WHERE b = 1 ORDER BY c

1. 索引 idx_c 是按 c 排序的,不是按 b

索引结构大概是:

复制代码
c=1 → 行地址
c=2 → 行地址
c=3 → 行地址
...

不按 b 分组/排序

2. 用 idx_c 的执行过程会是:

  • 遍历整个 c 索引(所有 c 值)
  • 每一行都要回表去判断:b 是不是等于 1
  • 筛选完后,结果集还要再按 c 排序(本来 c 有序,但中间被 b 过滤打断,不是连续的)

→ 优化器一算:回表 + 过滤 + 还要排序,成本比全表扫描还高

→ 干脆:放弃索引,直接全表扫描,然后 filesort

一句话:ORDER BY c 需要 c 整体连续有序,但 WHERE b=1 会把 c 的连续顺序"切碎",用 c 索引得不偿失。


三、为什么 WHERE b=1 GROUP BY c 反而能用 idx_c

语句:

sql 复制代码
WHERE b = 1 GROUP BY c

1. GROUP BY 的本质:按 c 去重 + 分组

只要数据按 c 有序,就能:

  • 顺序扫 c 索引
  • 遇到新 c → 开新组
  • 同 c 连续出现 → 合并到同一组

不需要严格"所有行都满足 b=1"再分组,可以边扫边过滤、边分组。

2. 用 idx_c 的执行逻辑(松散/紧凑索引扫描)

  • c 从小到大扫 idx_c
  • 每一行:回表看 b=1
    • 是 → 计入当前 c 组
    • 否 → 跳过
  • c 变了 → 输出上一组,开新组

优点:

  • 天然按 c 有序,不需要 filesort
  • 分组逻辑和索引顺序完美匹配
  • 优化器认为:比全表扫描快

所以:GROUP BY c 能利用 c 索引的有序性,即使 WHERE 有 b 过滤,依然划算。


四、一句话总结(面试直接背)

  • WHERE b=1 ORDER BY c
    c 索引有序被 b 过滤打乱,用索引要大量回表+排序,成本高 → 放弃索引,全表扫描。
  • WHERE b=1 GROUP BY c
    GROUP BY 只需要 c 有序去重分组,可以边扫边过滤,避免 filesort → 选择 c 单列索引。
相关推荐
倔强的石头_3 天前
《Kingbase护城河》——数据库存储空间全景探测与精细化瘦身实战
数据库
云技纵横3 天前
唯一索引 INSERT 死锁实战:5 秒复现交叉插入的 S 锁循环等待
sql·mysql
沉默王二3 天前
面试官:RAG 不用向量数据库,用 MySQL 硬扛?我:100 万向量不是很轻松?
mysql·面试·ai编程
冬奇Lab3 天前
每日一个开源项目(第134篇):Zvec - 阿里开源的嵌入式向量数据库,向量搜索界的 SQLite
数据库·人工智能·llm
小猿姐3 天前
MySQL Top 10 热点问题 AI 运维实战:从内核诊断到云原生运维
mysql·云原生·aiops
ClouGence4 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
云技纵横4 天前
Gap Lock 死锁实战:5 秒在本地复现 MySQL 间隙锁死锁
后端·mysql
无响应de神4 天前
三、用户与权限管理
数据库·mysql
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql