MySQL8.0新特性~最左前缀匹配原则被打破了

测试

在MySQL8.0.25和mysql5.7.33中创建如下

language 复制代码
CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRIMARY KEY(f1, f2));
INSERT INTO t1 VALUES
  (1,1), (1,2), (1,3), (1,4), (1,5),
  (2,1), (2,2), (2,3), (2,4), (2,5);
INSERT INTO t1 SELECT f1, f2 + 5 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 10 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 20 FROM t1;
INSERT INTO t1 SELECT f1, f2 + 40 FROM t1;
ANALYZE TABLE t1;

在mysql8.0.25中执行

language 复制代码
mysql> EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |   53 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------+
1 row in set, 1 warning (0.00 sec)

在mysql5.7.33中执行

language 复制代码
mysql> EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | NULL          | PRIMARY | 8       | NULL |  160 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

可以看到在8.0版本中使用了range的扫描方式

Using where; Using index for skip scan

原理

在MySQL-8.0.13版本增加的跳跃范围扫描特性,就是针对类似的场景的优化,跳跃范围扫描在这个示例中实际是针对每一个f1字段的值,进行了范围扫描,即进行了多次范围扫描。

针对这个示例,具体的跳跃范围扫描过程如下:

1.获取联合索引中第一个字段f1的第一个值:f1 = 1

2.将获取到的值和WHERE条件中的f2的条件组合:f1 = 1 AND f2 > 40

3.执行这个范围扫描查询

4.获取联合索引中第一个字段f1的第二个值:f1 = 2

5.将获取到的值和WHERE条件中的f2的条件组合:f1 = 2 AND f2 > 40

6.执行这个范围扫描查询

7.将两次范围扫描查询的结果合并返回给客户端

总结起来就是,把所有前置索引的值进行了穷举,再把每一个值都当做一个条件,拼成一个sql,再把这些sql union起来。

限制以及场景

复制代码
   表上至少存在一个联合索引([A_1,A_2...A_k],B_1,B_2...B_m,C,[,D_1,...,D_n]),其中A部分以及D部分可以为空,但是B和C部分不能为空。A_1,A_2..等代表字段值
复制代码
   只针对单表查询
复制代码
   查询中不包含GROUP BY或者DISTINCT
复制代码
   SELECT查询的字段全部被包含在索引组成部分,即符合覆盖索引规范
复制代码
   前缀A_1,A_2...A_k部分必须是可以被相等的常量
复制代码
   字段C上必须是一个范围条件,大于或大于等于,小于或小于等于
复制代码
   允许在D字段上有过滤条件,但是必须和C上的范围条件一起使用

跳跃范围扫描适用于联合索引中前导列distinct值较少,后续字段选择过滤性又比较好的场景,能更好的发挥跳跃范围扫描的作用。

开启或关闭

跳跃范围扫描默认是开启的,有两种方式可以关闭跳跃范围扫描特性:

通过修改optimizer_switcher变量值,默认MySQL是将optimizer_switcher中的skip_scan设置为on的,可以通过将skip_scan设置为off关闭跳跃范围扫描

通过Hint的方式关闭跳跃范围扫描特性:SELECT/*+ NO_SKIP_SCAN(t1 PRIMARY) */ f1, f2 FROM t1 WHERE f2 > 40;

相关推荐
资生算法程序员_畅想家_剑魔9 分钟前
Mysql常见报错解决分享-01-Invalid escape character in string.
数据库·mysql
霖霖总总1 小时前
[小技巧14]MySQL 8.0 系统变量设置全解析:SET GLOBAL、SET PERSIST 与 SET PERSIST_ONLY 的区别与应用
数据库·mysql
alonewolf_991 小时前
深入剖析MySQL索引底层:B+树、联合索引与跳跃扫描原理全解
数据库·b树·mysql
oMcLin2 小时前
如何在Debian 11上通过配置MySQL 8.0的分布式架构,提升跨区域数据同步的效率与延迟?
分布式·mysql·debian
计算机学姐2 小时前
基于SpringBoot的校园资源共享系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·spring·信息可视化
霖霖总总3 小时前
[小技巧23]全面理解 MySQL 的 WAL 机制:原理、影响与可观测性
数据库·mysql
冰暮流星4 小时前
sql语句之select语句的基本使用
数据库·sql·mysql
计算机毕设指导65 小时前
基于微信小程序的钓鱼论坛系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
·云扬·5 小时前
系统与MySQL核心监控指标及操作指南
android·数据库·mysql
霖霖总总5 小时前
[小技巧15]深入解读 MySQL sql_mode:从原理到实践,规避常见坑
sql·mysql