Mysql中联合索引的最左匹配

联合索引

通过将多个字段组合成一个索引,该索引就被称为联合索引。

比如,将商品表中的 product_no 和 name 字段组合成联合索引(product_no, name),创建联合索引的方式如下:

CREATE INDEX index_product_no_name ON product(product_no, name);

联合索引(product_no, name) 的 B+Tree 示意图如下(图中叶子节点之间我画了单向链表,但是实际上是双向链表,原图我找不到了,修改不了,偷个懒我不重画了,大家脑补成双向链表就行)。

 

可以看到,联合索引的非叶子节点用两个字段的值作为 B+Tree 的 key 值。当在联合索引查询数据时,先按 product_no 字段比较,在 product_no 相同的情况下再按 name 字段比较。

也就是说,联合索引查询的 B+Tree 是先按 product_no 进行排序,然后再 product_no 相同的情况再按 name 字段排序。

因此,使用联合索引时,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。在使用联合索引进行查询的时候,如果不遵循「最左匹配原则」,联合索引会失效,这样就无法利用到索引快速查询的特性了。

比如,如果创建了一个 (a, b, c) 联合索引,如果查询条件是以下这几种,就可以匹配上联合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

需要注意的是,因为有查询优化器,所以 a 字段在 where 子句的顺序并不重要。

但是,如果查询条件是以下这几种,因为不符合最左匹配原则,所以就无法匹配上联合索引,联合索引就会失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

上面这些查询条件之所以会失效,是因为(a, b, c) 联合索引,是先按 a 排序,在 a 相同的情况再按 b 排序,在 b 相同的情况再按 c 排序。所以,b 和 c 是全局无序,局部相对有序的,这样在没有遵循最左匹配原则的情况下,是无法利用到索引的。

我这里举联合索引(a,b)的例子,该联合索引的 B+ Tree 如下(图中叶子节点之间我画了单向链表,但是实际上是双向链表,原图我找不到了,修改不了,偷个懒我不重画了,大家脑补成双向链表就行)。

 

可以看到,a 是全局有序的(1, 2, 2, 3, 4, 5, 6, 7 ,8),而 b 是全局是无序的(12,7,8,2,3,8,10,5,2)。因此,直接执行where b = 2这种查询条件没有办法利用联合索引的,利用索引的前提是索引里的 key 是有序的

只有在 a 相同的情况才,b 才是有序的,比如 a 等于 2 的时候,b 的值为(7,8),这时就是有序的,这个有序状态是局部的,因此,执行where a = 2 and b = 7是 a 和 b 字段能用到联合索引的,也就是联合索引生效了。

#联合索引范围查询

联合索引有一些特殊情况,并不是查询过程使用了联合索引查询,就代表联合索引中的所有字段都用到了联合索引进行索引查询,也就是可能存在部分字段用到联合索引的 B+Tree,部分字段没有用到联合索引的 B+Tree 的情况。

这种特殊情况就发生在范围查询。联合索引的最左匹配原则会一直向右匹配直到遇到「范围查询」就会停止匹配。也就是范围查询的字段可以用到联合索引,但是在范围查询字段的后面的字段无法用到联合索引

范围查询有很多种,那到底是哪些范围查询会导致联合索引的最左匹配原则会停止匹配呢?

接下来,举例几个范围查例子。

Q1: select * from t_table where a > 1 and b = 2,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree?

由于联合索引(二级索引)是先按照 a 字段的值排序的,所以符合 a > 1 条件的二级索引记录肯定是相邻,于是在进行索引扫描的时候,可以定位到符合 a > 1 条件的第一条记录,然后沿着记录所在的链表向后扫描,直到某条记录不符合 a > 1 条件位置。所以 a 字段可以在联合索引的 B+Tree 中进行索引查询。

但是在符合 a > 1 条件的二级索引记录的范围里,b 字段的值是无序的。比如前面图的联合索引的 B+ Tree 里,下面这三条记录的 a 字段的值都符合 a > 1 查询条件,而 b 字段的值是无序的:

  • a 字段值为 5 的记录,该记录的 b 字段值为 8;
  • a 字段值为 6 的记录,该记录的 b 字段值为 10;
  • a 字段值为 7 的记录,该记录的 b 字段值为 5;

因此,我们不能根据查询条件 b = 2 来进一步减少需要扫描的记录数量(b 字段无法利用联合索引进行索引查询的意思)。

所以在执行 Q1 这条查询语句的时候,对应的扫描区间是 (2, + ∞),形成该扫描区间的边界条件是 a > 1,与 b = 2 无关。

因此,Q1 这条查询语句只有 a 字段用到了联合索引进行索引查询,而 b 字段并没有使用到联合索引

我们也可以在执行计划中的 key_len 知道这一点,在使用联合索引进行查询的时候,通过 key_len 我们可以知道优化器具体使用了多少个字段的搜索条件来形成扫描区间的边界条件。

举例个例子 ,a 和 b 都是 int 类型且不为 NULL 的字段,那么 Q1 这条查询语句执行计划如下,可以看到 key_len 为 4 字节(如果字段允许为 NULL,就在字段类型占用的字节数上加 1,也就是 5 字节),说明只有 a 字段用到了联合索引进行索引查询,而且可以看到,即使 b 字段没用到联合索引,key 为 idx_a_b,说明 Q1 查询语句使用了 idx_a_b 联合索引。

 

通过 Q1 查询语句我们可以知道,a 字段使用了 > 进行范围查询,联合索引的最左匹配原则在遇到 a 字段的范围查询( >)后就停止匹配了,因此 b 字段并没有使用到联合索引。

Q2: select * from t_table where a >= 1 and b = 2,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree?

Q2 和 Q1 的查询语句很像,唯一的区别就是 a 字段的查询条件「大于等于」。

由于联合索引(二级索引)是先按照 a 字段的值排序的,所以符合 >= 1 条件的二级索引记录肯定是相邻,于是在进行索引扫描的时候,可以定位到符合 >= 1 条件的第一条记录,然后沿着记录所在的链表向后扫描,直到某条记录不符合 a>= 1 条件位置。所以 a 字段可以在联合索引的 B+Tree 中进行索引查询。

虽然在符合 a>= 1 条件的二级索引记录的范围里,b 字段的值是「无序」的,但是对于符合 a = 1 的二级索引记录的范围里,b 字段的值是「有序」的(因为对于联合索引,是先按照 a 字段的值排序,然后在 a 字段的值相同的情况下,再按照 b 字段的值进行排序)。

于是,在确定需要扫描的二级索引的范围时,当二级索引记录的 a 字段值为 1 时,可以通过 b = 2 条件减少需要扫描的二级索引记录范围(b 字段可以利用联合索引进行索引查询的意思)。也就是说,从符合 a = 1 and b = 2 条件的第一条记录开始扫描,而不需要从第一个 a 字段值为 1 的记录开始扫描。

所以,Q2 这条查询语句 a 和 b 字段都用到了联合索引进行索引查询

我们也可以在执行计划中的 key_len 知道这一点。执行计划如下,可以看到 key_len 为 8 字节,说明优化器使用了 2 个字段的查询条件来形成扫描区间的边界条件,也就是 a 和 b 字段都用到了联合索引进行索引查询。

通过 Q2 查询语句我们可以知道,虽然 a 字段使用了 >= 进行范围查询,但是联合索引的最左匹配原则并没有在遇到 a 字段的范围查询( >=)后就停止匹配了,b 字段还是可以用到了联合索引的。

Q3: SELECT * FROM t_table WHERE a BETWEEN 2 AND 8 AND b = 2,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree?

 

Q3 查询条件中 a BETWEEN 2 AND 8 的意思是查询 a 字段的值在 2 和 8 之间的记录。不同的数据库对 BETWEEN ... AND 处理方式是有差异的。在 MySQL 中,BETWEEN 包含了 value1 和 value2 边界值,类似于 >= and =<。而有的数据库则不包含 value1 和 value2 边界值(类似于 > and <)。

这里我们只讨论 MySQL。由于 MySQL 的 BETWEEN 包含 value1 和 value2 边界值,所以类似于 Q2 查询语句,因此 Q3 这条查询语句 a 和 b 字段都用到了联合索引进行索引查询

我们也可以在执行计划中的 key_len 知道这一点。执行计划如下,可以看到 key_len 为 8 字节,说明优化器使用了 2 个字段的查询条件来形成扫描区间的边界条件,也就是 a 和 b 字段都用到了联合索引进行索引查询。

 

通过 Q3 查询语句我们可以知道,虽然 a 字段使用了 BETWEEN 进行范围查询,但是联合索引的最左匹配原则并没有在遇到 a 字段的范围查询( BETWEEN)后就停止匹配了,b 字段还是可以用到了联合索引的。

Q4: SELECT * FROM t_user WHERE name like 'j%' and age = 22,联合索引(name, age)哪一个字段用到了联合索引的 B+Tree?

由于联合索引(二级索引)是先按照 name 字段的值排序的,所以前缀为 ‘j’ 的 name 字段的二级索引记录都是相邻的, 于是在进行索引扫描的时候,可以定位到符合前缀为 ‘j’ 的 name 字段的第一条记录,然后沿着记录所在的链表向后扫描,直到某条记录的 name 前缀不为 ‘j’ 为止。

所以 a 字段可以在联合索引的 B+Tree 中进行索引查询,形成的扫描区间是['j','k')。注意, j 是闭区间。如下图:

 

虽然在符合前缀为 ‘j’ 的 name 字段的二级索引记录的范围里,age 字段的值是「无序」的,但是对于符合 name = j 的二级索引记录的范围里,age字段的值是「有序」的(因为对于联合索引,是先按照 name 字段的值排序,然后在 name 字段的值相同的情况下,再按照 age 字段的值进行排序)。

于是,在确定需要扫描的二级索引的范围时,当二级索引记录的 name 字段值为 ‘j’ 时,可以通过 age = 22 条件减少需要扫描的二级索引记录范围(age 字段可以利用联合索引进行索引查询的意思)。也就是说,从符合 name = 'j' and age = 22 条件的第一条记录时开始扫描,而不需要从第一个 name 为 j 的记录开始扫描 。如下图的右边:

 

所以,Q4 这条查询语句 a 和 b 字段都用到了联合索引进行索引查询

我们也可以在执行计划中的 key_len 知道这一点。本次例子中:

  • name 字段的类型是 varchar(30) 且不为 NULL,数据库表使用了 utf8mb4 字符集,一个字符集为 utf8mb4 的字符是 4 个字节,因此 name 字段的实际数据最多占用的存储空间长度是 120 字节(30 x 4),然后因为 name 是变长类型的字段,需要再加 2 字节(用于存储该字段实际数据的长度值),也就是 name 的 key_len 为 122。
  • age 字段的类型是 int 且不为 NULL,key_len 为 4。

key_len 的显示比较特殊,行格式是由 innodb存 储引擎实现的,而执行计划是在server 层生成的,所以它不会去问 innodb 存储引擎可变字段的长度占用多少字节,而是不管三七二十一都使用 2 字节表示可变字段的长度。

毕竟 key_len 的目的只是为了告诉你索引查询中用了哪些索引字段,而不是为了准确告诉这个字段占用多少字节空间。

Q4 查询语句的执行计划如下,可以看到 key_len 为 126 字节,name 的 key_len 为 122,age 的 key_len 为 4,说明优化器使用了 2 个字段的查询条件来形成扫描区间的边界条件,也就是 name 和 age 字段都用到了联合索引进行索引查询。

 

通过 Q4 查询语句我们可以知道,虽然 name 字段使用了 like 前缀匹配进行范围查询,但是联合索引的最左匹配原则并没有在遇到 name 字段的范围查询( like 'j%')后就停止匹配了,age 字段还是可以用到了联合索引的。

综上所示,联合索引的最左匹配原则,在遇到范围查询(如 >、<)的时候,就会停止匹配,也就是范围查询的字段可以用到联合索引,但是在范围查询字段的后面的字段无法用到联合索引。注意,对于 >=、<=、BETWEEN、like 前缀匹配的范围查询,并不会停止匹配,前面我也用了四个例子说明了

来源于林老师的读书笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27915.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

剑指 Offer 09: 用两个栈实现队列

简单明了&#xff0c;带你直接看懂题目和例子。 输入&#xff1a; ["CQueue","appendTail","deleteHead","deleteHead"] 这里是要执行的方法&#xff0c;从左到右执行 [[],[3],[],[]]对应上面的方法&#xff0c;是上面方法的参数。CQ…

docker资源控制cgroup

一、CPU控制 cgroups&#xff0c;是一个非常强大的linux内核工具&#xff0c;他不仅可以限制被namespace隔离起来的资源, 还可以为资源设置权重、计算使用量、操控进程启停等等。所以cgroups (Control groups) 实现了对资源的配额和度量。 cgroups有四大功能: 资源限制:可以…

前后端交互三、Ajax加强

零、文章目录 前后端交互三、Ajax加强 1、XMLHttpRequest的基本使用 &#xff08;1&#xff09;什么XMLHttpRequest XMLHttpRequest&#xff08;简称 xhr&#xff09;是浏览器提供的 Javascript 对象&#xff0c;通过它&#xff0c;可以请求服务器上的数据资源。jQuery 中的…

企业电子招标采购系统源码java 版本 Spring Cloud + Spring Boot

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

JVM原理:JVM垃圾回收算法(通俗易懂)

目录 前言正文垃圾标记算法引用类型强引用软引用弱引用虚引用 引用计数法循环引用问题 根可达性分析法虚拟机栈&#xff08;栈帧的局部变量表&#xff09;中的引用方法区中类静态属性引用方法区中常量引用本地方法栈&#xff08;Native方法&#xff09;引用 垃圾回收算法标记清…

InstructGPT:Training language models to follow instructions with human feedback

Training language models to follow instructions with human feedback 通过人类反馈的微调&#xff0c;在广泛的任务中使语言模型与用户的意图保持一致 aligning language models with user intent on a wide range of tasks by fine-tuning with human feedback 实验动机 …

AssetStudio工程导入VS各种报错解决

AssetStudio下载地址&#xff1a;https://github.com/Perfare/AssetStudio 工程导入&#xff0c;生成解决方案&#xff0c;然后报了一堆错。让我们来一个一个的解决 这个错误&#xff0c;是缺少System.Runtime.InteropServices.RuntimeInformation.dll文件&#xff0c;下载并添…

20230612 set1打卡

哈希表理论基础 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

【leetcode】189.轮转数组

方法1&#xff1a; 先把原数组拷贝一份&#xff0c;然后先将原数组的后k个拷贝&#xff0c;再将前numsSize-k个拷贝 void rotate(int* nums, int numsSize, int k) {//可能存在k>numsSize的情况&#xff0c;先处理kk k % numsSize;//拷贝数组int arr[numsSize];int i 0;in…

计算机网络 | socket IPC(本地套接字domain)

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

大模型研究方向0611

文章目录 1. 大模型自身角度认知角度2.大模型的成本角度3. 大模型的评测角度4.大模型的安全角度5. 大模型结构角度 附上刘知远老师的见解&#xff1a;https://hub.baai.ac.cn/view/27283 1. 大模型自身角度认知角度 即大模型的理解&#xff0c;现在&#xff0c;大众普遍认为L…

0202性能分析-索引-MySQL

1 索引语法 创建索引 CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_column_name,...);Index_name&#xff1a;规范为idx_表名_字段名... 查看索引 SHOW INDEX FROM table_name;删除索引 DROP INDEX index_name ON table_name;按照下列要求&#xff0c;创建…

V5.0.X版本 EMQX安装、卸载 以及使用

V5.0.X版本 EMQX安装、卸载 以及使用 一、卸载二、下载安装2.1 下载2.2 安装2.2.1 rpm安装2.2.2 tar安装 2.3 测试启动 三、EMQX使用 回到目录    回到末尾 EMQX为大规模分布式物联网 MQTT 消息服务器。提供高效可靠连接海量物联网设备&#xff0c;实时处理分发消息与事件流数…

leangoo领歌敏捷工具中,如何快速查看项目内所有任务卡片

项目管理员能不能快捷的查看整个项目内的所有任务&#xff1f; 能不能快捷查看项目内某一个成员的所有任务&#xff1f; 能不能快捷的在项目内通过一些条件选择查看任务&#xff1f; 可以导出项目内某一个人的所有任务吗&#xff1f;方便做一些统计 等等... 这些现在Leang…

武职301班-day01

实现永和小票页面 效果图 问题分析 把一个大的开发任务&#xff0c;先进行任务分析&#xff0c;把完成这个任务需要的技术点和开发步骤写出来。 开发分析 特点&#xff1a; htmlcss实现 1&#xff09;小票有宽度限制 2&#xff09;字体大小不一样&#xff0c;大部分字体大小…

解决获取taro全局配置文件失败,找不到配置文件失败问题

问题&#xff1a;这会导致项目初始化不成功&#xff0c;即要用vuets生成项目的话&#xff0c;依旧是wxml&#xff0c;js的文件&#xff0c;而不是vue文件 解决一&#xff1a;首先找到配置文件目录 删除taro开头的三项文件&#xff0c;再去node_modules下删除tarojs 然后去终…

Python的基础语法知识

1、变量 变量是一个代号&#xff0c;它代表的是一个数据。 在Python中&#xff0c;定义一个变量的操作包含两个步骤&#xff1a; ①为变量起一个名字 ②为变量指定其所代表的数据 这两个步骤在同一行代码中完成。 1.1 变量的命名规则 变量名可以由任意数量的字母、数字、下划…

实验五 标准ACL的配置【网络安全】

实验五 标准ACL的配置【网络安全】 前言推荐实验五 标准ACL的配置问题方案步骤 最后 前言 2023-6-8 18:54:22 以下内容源自《【网络安全】》 仅供学习交流使用 推荐 配置标准ACL 实验五 标准ACL的配置 问题 络调通后&#xff0c;保证网络是通畅的。同时也很可能出现未经…

vscode Delete `␍⏎·····`

在公司电脑首次拉取代码的时候&#xff0c;好多代码报错&#xff0c;在网上查了博客&#xff0c;确定是换行导致&#xff0c;但是参考网上的解决办法&#xff0c;没有解决&#xff0c;不管是设置 auto 还是命令行执行什么什么的&#xff0c;都不管用 下面介绍我的办法 首先&a…

CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)

原文网址&#xff1a;CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CloudFlare的CDN如何自定义第三方IP。 概述 CloudFlare官网接入域名的方式只能是 NS 接入&#xff0c;这样默认DNS服务器只能改为CloudFlare的D…