索引跳跃扫描(Index Skip Scan)是MySQL 8.0.13+支持的优化机制,允许在复合索引非最左列上走索引,前提是前导列基数极低、WHERE仅含后缀列等值条件、优化器估算代价更低且引擎为InnoDB。

索引跳跃扫描(Index Skip Scan)不是自动生效的“银弹”,它只在联合索引最左列基数极低、查询条件完全跳过该列且仅用后缀列等值匹配时,才可能被优化器选中。盲目依赖它,反而会掩盖本该建的单列索引。
为什么EXPLAIN里没看到Using index skip scan?
常见错误现象是:明明建了(status, created_at)索引,执行WHERE created_at = '2024-01-01'却还是type: ALL或key: NULL。这通常因为:
- 前导列
status实际有几百个不同值(比如订单状态存的是毫秒级时间戳或UUID),不满足“低基数”硬条件 - 查询里混入了
status IS NOT NULL或status != 'archived'——只要出现前导列,就退化为普通最左前缀扫描,跳扫直接失效 - 统计信息过期:
SELECT COUNT(DISTINCT status) FROM orders返回 87,但ANALYZE TABLE orders没跑过,优化器仍按旧数据估算成本 - 隐式类型转换:比如
created_at是DATETIME,但传入字符串'2024-01-01'没带时分秒,MySQL可能做截断比较,导致索引无法对齐
怎样验证跳扫是否真被启用?
别信传统EXPLAIN的Extra字段——它可能只写Using where,掩盖真相。必须用树形格式:
EXPLAIN FORMAT=TREE SELECT * FROM orders WHERE created_at = '2024-01-01';
如果输出中明确出现-> Index skip scan或using_index_skip_scan,才算真正触发。否则就是没走。注意:USE INDEX (idx_status_time)这类提示对跳扫完全无效,它不能被强制启用。
什么情况下该立刻放弃跳扫,改用单列索引?
当遇到以下任一情况,说明跳扫不适合你,硬上只会拖慢查询:
- 前导列
COUNT(DISTINCT)超过 20(比如user_id、order_no这种天然高基数字段) - 查询要
ORDER BY created_at或GROUP BY created_at——跳扫结果是多次子扫描拼起来的,全局无序,MySQL必须额外排序 - SELECT列表包含大量非索引列(如
SELECT *),每次子扫描都要回表,I/O放大倍数 =COUNT(DISTINCT 前导列) - 该列本身就被高频单独查询,建单列索引的成本远低于维护一个“勉强能跳”的联合索引
跳扫的本质是“用CPU换I/O”:它把一次全表扫描,拆成N次小索引扫描。N太大会让延迟翻倍,而N太小又不如直接建单列索引干脆。真正容易被忽略的点是——它从不改变索引结构,只改变扫描方式;所以即使启用了,也别指望它解决回表、排序、覆盖不足这些老问题。


















