MySQL 联合索引:范围定位与 ICP 过滤的判断
一、核心区别
核心区别:范围定位减少索引扫描量,ICP 减少回表量。
二、联合索引的排序规则
假设存在联合索引:
INDEX idx_user_status_age (user_id, status, age)索引数据:
user_id status age
------------------------
zhang01 1 18
zhang01 1 20
zhang01 2 19
zhang01 2 21
zhang02 1 18
zhang02 1 20
zhang02 2 19
zhang02 2 21联合索引的排序规则:
首先按照
user_id排序。user_id相同时,再按照status排序。user_id和status都相同时,再按照age排序。
关键:后面的字段只有在前面的字段值确定时,才能形成连续的有序范围。
三、如何判断范围定位还是 ICP?
示例一:等值 + 范围,可以继续定位
SELECT *
FROM user
WHERE user_id = 'zhang01'
AND status > 3;原因:
user_id 已经固定为 zhang01,因此这一组里的 status 是有序的。
zhang01 → status: 1, 2, 3, 4, 5, 6
↑
从 >3 开始扫描MySQL 可以直接定位到 status > 3 的范围,不需要扫描前面不符合条件的索引记录。
示例二:LIKE 范围 + 其他条件,通常使用 ICP
SELECT *
FROM user
WHERE user_id LIKE 'zhang%'
AND status > 3;原因:
LIKE 'zhang%' 会匹配多个不同的 user_id。
user_id status
-----------------
zhang01 1
zhang01 5
zhang02 2
zhang02 6
zhang03 3
zhang03 7虽然同一个 user_id 下面的 status 是有序的,但多个 user_id 放在一起:
status: 1, 5, 2, 6, 3, 7status 在整个扫描范围内不是连续有序的,因此通常无法继续缩小索引扫描范围。
但可以在扫描索引时判断 status > 3,通过 ICP 提前过滤,减少回表。
注意:不是 status 没有排序,而是它只在相同 user_id 的分组内部有序。
示例三:范围条件后面的字段,通常不能继续定位
SELECT *
FROM user
WHERE user_id = 'zhang01'
AND status > 3
AND age = 20;原因:
虽然 user_id 已经固定,但是 status > 3 包含多个不同的 status 值。
user_id status age
------------------------
zhang01 4 18
zhang01 4 20
zhang01 5 19
zhang01 5 20
zhang01 6 21age 只在相同 status 的分组中有序。
把 status > 3 的记录放在一起,age 不是整体有序的,因此通常不能继续缩小连续扫描范围,只能作为 ICP 过滤条件。
示例四:前面都是等值,最后一列可以范围定位
SELECT *
FROM user
WHERE user_id = 'zhang01'
AND status = 4
AND age > 20;原因:
user_id 和 status 都已经固定,age 在这个分组内连续有序,因此可以直接进行范围定位。
四、判断口诀
针对普通联合索引的连续范围扫描:
前面等值,可以继续定位;遇到范围,后面通常转为索引过滤。
判断步骤:
按照联合索引的字段顺序分析,而不是 WHERE 的书写顺序。
前面的字段是等值条件,后面的字段可以继续参与定位。
遇到
>、<、BETWEEN、前缀LIKE等范围条件时,要判断后面的字段能否继续缩小扫描区间;通常不能。后面的字段如果包含在索引中,可以用于 ICP 等索引层过滤;如果不在索引中,通常需要回表后过滤。
注意:这是常见连续范围扫描的判断方法,不是绝对规则。例如 IN 可以形成多个索引区间,BETWEEN 的边界也可能利用后续索引列。是否真正启用 ICP,要看 EXPLAIN 的 Using index condition。
最终记住:能利用索引有序性缩小扫描范围的是定位;不能缩小范围、但能在索引扫描过程中提前筛选记录的,才是 ICP 过滤的典型场景。
