mysql索引最左匹配原则的理解?(绝对牛逼)

news2024/12/24 3:18:06

前言

测试的时候就发现不对劲

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `cid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_cid_INX` (`name`,`cid`),
  KEY `name_INX` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

随便建了一个student表做测试。

create INDEX name_cid_INX ON student(name,cid);
create INDEX name_INX ON student(name);

建了两个索引,故意这样建的。
执行1:

EXPLAIN SELECT * FROM student WHERE    name='小红';

依据mysql索引最左匹配原则,两个索引都匹配上了,这个没有问题。。
执行2:

EXPLAIN SELECT * FROM student WHERE   cid=1;

EXPLAIN SELECT * FROM student WHERE   cid=1 AND name='小红';

为什么还能匹配索引。

这是知乎上的一个问题,今天我们就带着这个问题来深入理解一下mysql索引最左匹配原则。

发车

其实我觉得不用刻意的去背最左前缀法则,最重要的是理解复合索引的索引树是如何构建的,理解了符合索引树是如何构建的最左前缀法则自然了然于胸!

直接上图:(我们约定以名字的字母升序排列)

我们就会得到上面这样索引结构(当然树的深度也有可能是别的层数,为了方便理解我就画了两层),我们可以看到这棵树完全是以name的顺序构建的树,只有在name的排序规则不能排的情况下才会用到cid比如树中有两个小张。

当你使用cid去查的时候,他不知道如何取数据,从图中我们也可以看出好多个地方的cid是相同的,这便是最左前缀法则的依据所在!

针对第一句:

EXPLAIN SELECT * FROM student WHERE name='小红';

使用到索引,这个应该我们都能理解,就算不能理解,背八股文也能知道这个必须会用的索引,从图中我们也可以看出

可能使用到的索引有两个,但是最终还是选择了name_cid_INX,思考一下为什么?(后面会讲)

前面我们已经说了,就算不知道为什么会命中索引,我们通过背八股文也能知道他会命中索引,但是不理解纯背诵默写套公式就很累,今天我就来讲解一下为什么会一定命中索引!

首先我们需要知道mysql的索引分为两种,聚簇索引和二级索引,聚簇索引存完整数据记录,二级索引只存索引列和行记录的主键值!

二级索引图如下(约定以字母升序排列):

聚簇索我懒得画了!

从图中我们可以看出,这个所以结构就是以name去排序然后生成的树,所以当你使用name作为查询条件的时候必然就会用到索引!

接着我们继续讲:

EXPLAIN SELECT * FROM student WHERE   cid=1 AND name='小红';

为什么这一句明明我们cid排在前面怎么也会使用到索引呢?

先看图:

一条sql执行会经历这么些个阶段,其中有一个工作的部件叫做“优化器”,这个优化器会调整语法的顺序,
于是乎虽然我们写成了SELECT * FROM student WHERE cid=1 AND name='小红';

他也会把他优化成:SELECT * FROM student WHERE name='小红' and cid=1 ;

这下不就满足了最左前缀法则了嘛,对吧?

其实我们也可以通过mysql的OPTIMIZER_TRACE去证明!

那如何证明呢?

第一步:
set session optimizer_trace="enabled=on", end_markers_in_json=on;

第二步:

 SELECT * FROM student WHERE   cid=1 AND name='小红';
 SELECT * FROM information_schema.OPTIMIZER_TRACE;

注意:这两句要一起执行!不然会出现这种情况:

der都没有!

正常执行以后我们就可以看到完整的执行过程!

文件很长,全部粘贴影响阅读,
我直接粘贴最重要的部分


"condition_processing": {
              "condition": "WHERE",
              "original_condition": "((`student`.`cid` = 1) and (`student`.`name` = '小红'))",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "((`student`.`name` = '小红') and multiple equal(1, `student`.`cid`))"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "((`student`.`name` = '小红') and multiple equal(1, `student`.`cid`))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "((`student`.`name` = '小红') and multiple equal(1, `student`.`cid`))"
                }
              ] /* steps */
            } /* condition_processing */

我们可以看到 "resulting_condition": "((student.name= '小红') and multiple equal(1,student.cid))"

表明已经优化成了SELECT * FROM student WHERE name='小红' and cid=1 ; 所以走了索引!

好好好!

我们接着看这句:
EXPLAIN SELECT * FROM student WHERE cid=1;

为什么这句使用EXPLAIN去分析也会用到索引呢?

啊?

这也用到索引? 你在逗我吗? 我背了那么久的索引最左匹配原则,难道错了吗?

别着急,还有更炸裂的!

我们可以看到possible_keys这一列为空,这一列的意思是“可能用到的索引”,key为实际使用的索引,按照逻辑讲第一个为null

那么第二个也应该为null才对,但是现在却出现了前面为空,后面不为空的情况,这是为啥?

需要理解中这个需要回到我们的表结构去看问题!

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `cid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_cid_INX` (`name`,`cid`),
  KEY `name_INX` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

然后name_cid_INX对于应的索引树不就是它吗?

我们再仔细观察一下,这颗索引树不久已经包含这张表的所有字段嘛对吧?

也就是说我们要查询的列不就是直接可以从索引中能返回吗?

这不就是传说中的索引覆盖吗?

这种情况二级索引往往比聚集索引小,所以mysql可能会选择顺序遍历这个二

级索引直接返回,所以才出现了EXPLAIN SELECT * FROM student WHERE cid=1;依然走索引的情况!

这里的本质是产生了索引覆盖!!

OPTIMIZER_TRACE分析工具显示:

{
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ] /* plan_prefix */,
                "table": "`student`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "scan",
                      "rows": 1,
                      "cost": 1.2,
                      "chosen": true
                    }
                  ] /* considered_access_paths */
                } /* best_access_path */,
                "cost_for_plan": 1.2,
                "rows_for_plan": 1,
                "chosen": true
              }
            ] /* considered_execution_plans */
          },

诚然OPTIMIZER_TRACE分析工具的"access_type": "scan"确实显示了scan,但它指的是扫描了二级索引树,并不是扫描了整张表!

如果们这时候换另一张表:

 CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';

在这张表中我们构建了一个:idx_name_age_position联合索引,这时候如果们再分析这条sql的执行计划:

EXPLAIN SELECT * FROM employees WHERE age=20

这时候这个查询并不遵守最左前缀法则也不能实现索引覆盖
我们可以看到:

这时候并不会走索引!

我们再改一下: EXPLAIN SELECT id FROM employees WHERE age=20

这时候虽然不遵守最左前缀法则,但是产生了索引覆盖!

我们可以看到,这时候还是走了索引!

我们继续回到开头那个问题:

EXPLAIN SELECT * FROM student WHERE name='小红';

这句sql可能使用到的索引有两个,但是最终还是选择了name_cid_INX

这便是因为name_cid_INX产生了索引覆盖,不需要再回表,查询效率高所以选择了它!

打完收工!!! 希望对你有帮助!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue封装请求、合并js、合并多个js

vue封装请求、合并js、合并多个js 作为一个后端开发,写前端时发现,每次导入api接口都会有一堆代码,像下面这样: import {footprintList, footprintDelete} from /api/userApi.js import {addressList} from /api/userApi.js impor…

CPU资源控制

一、CPU资源控制定义 cgroups(control groups)是一个非常强大的linux内核工具,他不仅可以限制被namespace隔离起来的资源, 还可以为资源设置权重、计算使用量、操控进程启停等等。 所以cgroups(control groups&#xf…

西圣、小米、倍思开放式耳机好用吗?详细测评对比性能王者

身为一名在数码科技领域有着丰富经验的测评师,我深入接触过各种开放式耳机。在众多开放式耳机品牌中,西圣、小米和倍思三款产品以其出色的性能和独特的设计,受到市场的广泛议论,今天我将为大家带来这三款开放式耳机的详细测评对比…

最新AI创作系统ChatGPT网站源码Midjourney-AI绘画系统,Suno-v3-AI音乐生成大模型。

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图文教程吧。已支持GPT…

Laravel 6 - 第十二章 控制器

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

javaScript基础3

javaScript 一.对象1.概念2.创建对象的三种方法(1).字面量创建(利用{})(2)变量、属性、函数、方法的区别(3).new Object创建(4).构造函数 3.new关键字的执行过程4.遍历对象(for..in) 二.内置对象1.了解2.math对象3.日期对象(构造函…

挖矿木马基础知识

文章目录 一、概述二、挖矿介绍三、挖矿的收益四、挖矿木马的传播方式漏洞利用NSA武器的使用无文件挖矿利用网页挂马暴力挖矿病毒黑吃黑 五、防范建议六、学习参考 一、概述 比特币(Bitcoin)的概念最初由中本聪在 2008年11月1日提出,并于 2009年1月3日正式诞生。根…

Shell全套课程2小时速通从小白变高手

1.Shell概述 1.1为什么要学shell ​ 1.看懂运维人员编写的shell脚本 ​ 2.偶尔会编写一些简单的shell程序来管理集群,提高开发效率 1.2 Shell介于外层应用和LInux内核之间;用来操作Linux内核; Shell是一个命令行解释器,它接收…

算法课程笔记——如何进制转换

python特性 八、为什么负数的补码的求法是反码1 因为负数的反码加上这个负数的绝对值正好等于1111,在加1,就是10000,也就是四位二进数的模,而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值得到它的补码&…

2024最新SSL证书在线申请系统源码 | 支持API接口 支持在线付费 二开优化版

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 2024最新SSL证书在线申请系统源码 | 支持API接口 支持在线付费 二开优化版 最新SSL证书在线申请系统源码 | 支持API接口 SSL证书保证网络安全的基本保障。向您介绍我们的在线生成SSL…

权威解析Spring框架九大核心功能(续篇):专业深度,不容错过

作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 👇🏻 避免错过下次更新 Springboot项目精选实战案例 更多项目:CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

速度与激情:超高速--100G网卡篇

在数字化时代,信息传输的速度和效率成为了各个领域的关键。在这个快节奏的世界里,网络连接的快慢直接影响着工作效率、生活质量甚至是创新能力。而在网络连接技术中,网卡的作用举足轻重。近年来,随着网络技术的不断发展&#xff0…

路由引入,路由过滤,路由策略简单实验

实验要求: 1、按照图示配置 IP 地址,R1,R3,R4 上使用 1oopback 口模拟业务网段 2、R1 和 R2 运行 RIPv2,R2,R3和R4 运行 OSPF,各自协议内部互通 3、在 RIP 和 OSPF 间配置双向路由引入&#x…

在PostgreSQL中,如何创建一个触发器并在特定事件发生时执行自定义操作?

文章目录 解决方案示例代码1. 创建自定义函数2. 创建触发器 解释 在PostgreSQL中,触发器(trigger)是一种数据库对象,它能在特定的事件(如INSERT、UPDATE或DELETE)发生时自动执行一系列的操作。这些操作可以…

短期斩获多个访问学者邀请函|高校教师获批CSC赴伦敦大学学院

B老师申报的是2023年CSC西部/地方合作项目,因申报在即,所以时间是第一要素,国家定位在英国及澳大利亚。经过努力,我们先后获得英国布里斯托大学、伦敦大学学院及澳大利亚昆士兰大学等多个邀请函,最终其选择了英国伦敦大…

CSS学习(选择器、盒子模型)

1、CSS了解 CSS:层叠样式表,一种标记语言,用于给HTML结构设置样式。 样式:文字大小、背景颜色等 p标签内不能嵌套标题标签。 2、CSS编写位置 1、行内样式(内联样式):在标签里添加样式&#…

谷歌搜索SEO优化需要做什么?

最基本的要求,网站基础要优化好,让你的网站更加友好地服务于用户和搜索引擎,首先你要保证你的网站也适配手机端,现在手机端,如果你的网站在手机上打开慢,或者没有适配手机端,让用户用手机看着电…

Git merge的版本冲突实验

实验目的 发现 两个分支的 相同文件 怎样被修改 才会发生冲突? 实验过程 1.初始状态 现在目前有1.py、2.py两个文件,已经被git管理。现在我想制造冲突,看怎样的修改会发生冲突,先看怎么不会发生冲突。 目前仓库里的版本是这样…

【MySQL】A01、性能优化-参数监控分析

1、参数监控 1.1、MySQL command 查看 mysql>SHOW STATUS; (服务器状态变量,运行服务器的统计和状态指标) mysql> SHOW VARIABLES;(服务器系统变量,实际上使用的变量的值) mysql> SHOW STATUS …

2024年 团体程序设计天梯赛个人总结

前言: 这是一个悲伤的故事~ 🏆题目传送门 ⭐L1一阶题⭐L1-097 编程解决一切(5分)⭐L1-098 再进去几个人(5分)⭐L1-099 帮助色盲(10分)⭐L1-100 四项全能(10 分&#xff0…