用 SKIP_SCAN 提示让单表范围条件利用联合索引的后缀列
来源:17golang原创
时间:2026-11-05 11:46:58 197浏览 收藏
如果联合索引的第一列没有出现在条件里,MySQL 通常无法像普通 range 那样直接定位后缀列范围。单表查询可以尝试使用优化器提示 SKIP_SCAN:让优化器跳过联合索引前导列的不同取值,再对后缀列的范围条件分别做子范围扫描。
写法是把提示放在SELECT后面,例如SELECT /*+ SKIP_SCAN(t1 PRIMARY) */ ...。它只是在符合 Skip Scan 条件时强制尝试该访问方法;索引或查询形状不适用时,提示仍可能被忽略,不能把它当成无条件的FORCE INDEX。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/range-optimization.html
下面用 MySQL 8.4 手册中的同类结构解释提示语义。文章只讨论单表范围条件、联合索引后缀列和 SKIP_SCAN,不延伸到普通索引强制、连接查询或实际压测结果。
为什么没有前导列条件就难以直接做 range
假设表上有联合索引 PRIMARY(f1, f2),查询只写 WHERE f2 > 40。B-Tree 的键顺序先按 f1,再按 f2 排列;没有 f1 的限制时,所有 f1 值混在索引的不同区段里,条件本身不能形成一个连续的单一索引区间。
这不代表后缀列完全没有选择价值。Skip Scan 的思路是把每个不同的 f1 值视为一个前缀,再分别构造 f1 = 某值 AND f2 > 40 的范围。它跳过每个前缀中不满足后缀条件的部分,而不是把整个索引当作一条平面列表逐行过滤。

用 SKIP_SCAN 提示指定索引
优化器提示必须写在语句初始关键字之后的 /*+ ... */ 注释中。可以指定表和索引,也可以只指定表让优化器在可用索引中选择。下面的主键由 f1, f2 组成,查询只投影索引中的两列,提示明确指向 PRIMARY:
-- 只查询联合索引覆盖的列,并明确尝试 PRIMARY 上的 Skip Scan
SELECT /*+ SKIP_SCAN(t1 PRIMARY) */
f1, f2
FROM t1
WHERE f2 > 40;
-- 只指定表时,让优化器在适用索引中选择 Skip Scan 候选
SELECT /*+ SKIP_SCAN(t1) */
f1, f2
FROM t1
WHERE f2 BETWEEN 40 AND 60;
SKIP_SCAN(t1 PRIMARY) 不是把 f2 变成独立索引,也不是改写 SQL 为很多个 OR 条件。它影响的是当前语句的访问方法候选,实际是否能用仍由索引结构、谓词和代价判断决定。
哪些查询形状适合这个提示
按照 MySQL 8.4 参考手册,Skip Scan 需要联合索引,并且查询只涉及一张表;查询不能使用 GROUP BY 或 DISTINCT,引用的列应当都在该索引中;前导列上的条件需要是常量等值条件(包括 IN()),同时要有后续列的范围条件。对没有显式前导列条件的典型结构,前导部分可以为空,但这并不放宽其它限制。
| 检查项 | 适合的形状 | 常见不适用原因 |
|---|---|---|
| 表范围 | 单表 SELECT | 多表 JOIN 或复杂子查询 |
| 索引列 | 投影列和过滤列都由联合索引覆盖 | 必须回表读取大量非索引列 |
| 谓词 | 后缀列存在 >、 | 只有普通函数条件或无法形成范围 |
| 结果形状 | 没有 DISTINCT、GROUP BY | 需要分组或去重的结果 |
这里的“适合”是结构上的候选条件,不等于一定更快。前导列的不同值很多、后缀范围很宽时,Skip Scan 仍可能需要访问许多子范围,优化器可能选择其它计划。

怎样确认提示真的被采用
先用 EXPLAIN 查看计划的 Extra。官方文档指出,使用 Skip Scan 时通常会出现 Using index for skip scan,对应索引也应出现在 possible_keys 中。这个检查只用于理解计划,不要把提示文字本身当成结果保证。
-- 先查看优化器是否把 Skip Scan 作为访问路径
EXPLAIN
SELECT /*+ SKIP_SCAN(t1 PRIMARY) */
f1, f2
FROM t1
WHERE f2 > 40;
-- 需要临时关闭 Skip Scan 时,只影响当前会话
SET SESSION optimizer_switch = 'skip_scan=off';
如果 Extra 没有出现该标记,先检查查询是否仍然只读取索引列、是否意外加入了分组或去重、范围条件是否落在正确的后缀列,以及索引名和表别名是否写对。优化器提示只作用于当前语句,而 optimizer_switch 的 skip_scan 标志控制会话范围;语句提示的优先级高于开关,但前提是提示本身适用。
提示被忽略时怎么处理
官方语义是:SKIP_SCAN 会强制优化器尝试指定表或索引的 Skip Scan,但索引对该语句不适用时可以忽略。提示被忽略不应通过不断叠加其它 hint 来“修复”,而应回到查询形状和数据分布。
实际排查可以按三个方向收窄:第一,确认表别名和索引名一致,主键索引使用 PRIMARY;第二,确认 SELECT 的列真的由目标联合索引覆盖;第三,暂时去掉提示对比普通计划,再根据 EXPLAIN 的 key、possible_keys 和 Extra 判断是否值得保留。不要仅凭“用了索引”就推断一定减少了总耗时。
还要注意版本边界。SKIP_SCAN 是优化器提示,不是通用 SQL 语法;部署到不同 MySQL 版本前,应在目标版本的参考手册和实际计划上重新确认。若业务查询后续增加了回表列、连接、分组或排序,也要重新评估,而不是沿用原来的提示。
小结
联合索引的前导列缺少条件时,普通范围访问不能直接把后缀列变成连续区间;Skip Scan 通过枚举前缀值并构造多个子范围,为这类单表查询提供另一种候选访问方式。使用时记住三点:提示写在 SELECT 后,索引和查询必须满足适用条件,最终是否采用要看 EXPLAIN,而不是只看 SQL 写法。
常见问题
SKIP_SCAN 和 FORCE INDEX 是一回事吗? 不是。前者控制 Skip Scan 访问方法,后者限制可用索引;两者解决的问题和适用边界不同。
只要写了 SKIP_SCAN 就一定会出现 Using index for skip scan 吗? 不一定。索引不适用、查询不满足条件或提示引用错误时,都可能看不到该访问方式。
后缀列有范围条件就应该强制 Skip Scan 吗? 不应该。还要考虑前导列基数、范围宽度、是否覆盖索引以及计划代价,先看 EXPLAIN 再决定。
-
368 收藏
-
449 收藏
-
486 收藏
-
111 收藏
-
419 收藏
-
205 收藏
-
203 收藏
-
291 收藏
-
260 收藏
-
368 收藏
-
148 收藏
-
102 收藏
-
137 收藏
-
488 收藏
-
428 收藏
-
152 收藏
-
498 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习