松散索引扫描(Loose Index Scan) 是 MySQL 优化器的一种高级查询策略,主要用于优化 GROUP BY 或 DISTINCT 查询。
它的核心思想是:不需要扫描索引中所有的行,而是跳过不符合条件的行,直接"跳跃"到每个分组的第一个或最后一个匹配项上。
1. 核心原理:从"地毯式搜索"到"跳跃式前进"
在传统的**紧凑索引扫描(Tight Index Scan)**中,数据库会扫描索引中的每一条记录。而松散索引扫描则利用了索引的有序性,只读取每个组中极少数的行。
直观对比:
假设你在一个按 (A, B) 排序的索引中查找每个 A 的最小值:
- 紧凑扫描 :读取
A=1的所有行找到最小值,再读取A=2的所有行......这就像翻完一整本书。 - 松散扫描 :读取
A=1的第一行(由于有序,第一行必是最小值),然后直接跳到 索引中A=2的起始位置。这就像直接看每章的目录。
2. 触发松散索引扫描的条件
MySQL 并不会对所有查询都使用这一策略,它通常需要满足以下严格条件:
- 单表查询:查询只涉及一张表。
- 前缀索引 :
GROUP BY或DISTINCT使用的列必须符合索引的最左前缀原则。 - 聚合函数限制 :只支持
MIN()和MAX()。且这些函数的参数必须是索引中的列,并且紧跟在GROUP BY列之后。 - 完整性 :索引中的所有列必须参与了查询(要么在
GROUP BY中,要么在MIN/MAX中)。
3. 一个具体的示例
假设有一张表 orders,索引为 (customer_id, order_date)。
sql
SELECT customer_id, MIN(order_date)
FROM orders
GROUP BY customer_id;
执行逻辑:
- 数据库定位到索引的头部。
- 获取第一个
customer_id(比如ID=100) 的第一条记录。 - 关键动作 :由于索引是排好序的,
ID=100的第一条记录对应的order_date一定是该客户最早的订单。 - 跳跃 :数据库直接通过索引树的 B+ 结构,寻找比
ID=100大的下一个customer_id。 - 重复此过程,直到索引结束。
4. 如何在 EXPLAIN 中识别?
当你运行 EXPLAIN 时,如果在 Extra 列中看到以下字样,说明松散索引扫描生效了:
Using index for group-by
这标志着查询性能得到了质的提升,因为数据库处理的数据行数从 全表记录数 降低到了 分组数。