如何优化mysql索引-最左前缀原则案例详解

2023-12-04

也许大多数人对索引优化的理解就是调优SQL。一般来说,是看它是否有索引,如果没有就给它添加索引。但不是这样的。#如何优化mysql索引#

如果想做好索引优化,就需要了解它的底层逻辑。

最左前缀原则

我们一般要优化复杂的SQL,而复杂的SQL一般会使用联合索引。说到联合索引的匹配规则,我们就逃不开这个:最左前缀规则。

简单解释一下,最左边前缀规则就是:索引的匹配从最左边的字段开始,只有匹配成功才能继续匹配到右边的下一个字段。

我们先来看看这个联合索引idx_nme_age_school。

假设我们现在有一张student表


  

r

复制代码

CREATE TABLE `students` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, `age` int NOT NULL, `school` varchar(255) NOT NULL, `address` varchar(255) NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_name_age_school` (`name`,`age`,`school`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3

idx_name_age_school的联合索引由name、age、school三个字段组织,字段顺序相同。

首先,我们都知道索引实际上是一种高效的数据结构。

那么对于索引idx_name_age_school,就会这样排序。

  • 名称字段从小到大排序。

  • 当name字段的值相同时,age字段从小到大排序。

  • 当age字段的值相同时,school字段从小到大排序。

如上图所示,顺序是从n_18到n_100,而名字为n_18的三个节点中,年龄从小到大排序,年龄相同时,学校也从小到大排序。

用这个联合索引来分析最左边前缀的规则:索引匹配时,必须先匹配name字段(最左边),然后才能匹配age字段(下一个)。只有年龄字段匹配成功,才能匹配学校字段。

这是因为联合索引中最左边的字段是有序的,所以当第一个字段相同时,第二个字段有序,当第二个字段相同时,第三个字段有序。

如果想用age字段直接查找数据是找不到的,因为age字段在联合索引中是无序的。

还是有点困惑?没关系,我们再分析以下案例。

如何命中索引

就是看索引会不会发挥作用,如果能发挥作用,那就是命中索引。

SQL1


  

sql

复制代码

EXPLAIN SELECT * FROM students WHERE NAME > 'n_18';

name字段是联合索引最左边的字段,所以会命中索引。

SQL2


  

ini

复制代码

EXPLAIN SELECT * FROM students WHERE age = 18;

Age字段不是联合索引的最左边字段,而且在索引中是乱序的,所以不使用索引,需要全表扫描

SQL3


  

ini

复制代码

EXPLAIN SELECT * FROM students WHERE NAME = 'n_18' AND age = 20;

name字段和age字段都会命中索引,因为当name字段相同时,age字段是有序的,所以age此时也能命中索引。

以上图为例。当定位到n_18时,可以直接定位到age=20的数据,不需要从age=18开始查找,所以索引在age字段中也发挥了作用。

深入分析

SQL4


  

ini

复制代码

EXPLAIN SELECT * FROM students WHERE age = 20 AND NAME = 'n_18';

和SQL3一样,name和age都会用到索引,最左边的前缀和你sql语句的位置无关,MySQL在执行过程中会自动调整位置,数据库优化器会自动将语句改为name = "n_18 “ and age = 20 。

SQL5


  

csharp

复制代码

explain select * from students where name > 'n_18' and age = 20;

只有name字段用到索引,age不会用到索引。 因为此时mysql的查询逻辑是定位到name=n_18最右边的一条数据,然后通过叶子节点的指针向右扫描遍历,所以索引对age字段没有影响。

SQL6


  

csharp

复制代码

explain select * from students where name >= 'n_18' and age = 20;

与SQL5类似,唯一的区别是name大于或等于。 此时,name和age都会被索引。

现在,我猜你一定有点困惑,正常情况下范围查找不会导致索引失败吗?

那么为什么age字段还能用到索引? 我们可以把这个SQL改成一种写法。


  

csharp

复制代码

explain select * from students where (name = 'n_18' and age = 20) or (name > 'n_18' and age = 20);

对于查询条件name = ‘n_18’和age = 20,name和age都能用到索引。

对于查询条件name > ‘n_18’和age = 20,只有name用到索引。

当两个查询条件组合时,两个字段都会被索引。

SQL7


  

sql

复制代码

explain select * from students where name like 'n_18%' and age = 10;

与 SQL6 相同,姓名和年龄都会用到索引。 但要注意,这条SQL在MySQL8下执行时不会走索引。

SQL8


  

sql

复制代码

explain select * from students where name between 'n_18' and 'n_50' and age = 10;

与 SQL7 相同,name和age都将被索引。 但要注意,这条SQL在MySQL8下执行时不会走索引。

至此,你应该对最左前缀规则有了深入的了解,更多的思路可以自己探索。

出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何优化mysql索引-最左前缀原则案例详解 的相关文章

  • 使用 android AudioTrack 在左或右扬声器中播放声音

    我正在应用程序中的 AudioTrack 的帮助下播放声音 但我想在特定扬声器 耳朵中播放声音 即左扬声器或右扬声器或两个扬声器 以下代码是我用来播放声音的 private AudioTrack generateTone double fr
  • 来自数据库的 jfreechart 散点图

    如何使用java中的jfreechart绘制mysql数据库表中数据的散点图 我使用过 Swing 库 任何链接都会有帮助 我搜索了谷歌但找不到理解的解决方案 如果您有代码 请提供给我 实际上我确实做了条形图并使用 jfreechart 绘
  • 条件触发器的Django迁移sql

    我想创建一个触发器 仅在满足条件时插入表 我尝试过使用 IF BEGIN END 和 WHERE 的各种组合 但 Django 每次都会返回 SQL 语法错误 这里 type user id指的是触发该事件的人 user id指的是接收到通
  • 从表中选择行,其中另一个表中具有相同 id 的行在另一列中具有特定值

    在 MySQL 中 如果我们有两个表 comments key value 1 foo 2 bar 3 foobar 4 barfoo and meta comment key value 1 1 2 1 3 2 4 1 我想得到来自以下人
  • 在 Android 中长按时从操作模式中删除后退/主页按钮

    我已经在里面长按实现了上下文操作模式recycler view 为此我打电话给ActionModeCallback从创建动作模式开始 创建动作模式时 默认显示后退箭头 检查如下 单击后退箭头 操作模式将关闭 现在我想要hide or rem
  • 如何从 Retrofit2 获取字符串响应?

    我正在做 android 正在寻找一种方法来执行超级基本的 http GET POST 请求 我不断收到错误 java lang IllegalArgumentException Unable to create converter for
  • 重写 URL,将 ID 替换为查询字符串中的标题

    我对 mod rewrite 很陌生 但我做了一些搜索 但找不到这个问题的答案 我有一个网站 它只有一个 PHP 页面 根据查询字符串中传递给它的 ID 提供数十页内容 我想重写 URL 以便此 ID消失并替换为从数据库中提取的页面标题 例
  • 如何将 android.net.Uri 转换为 java.net.URL? [复制]

    这个问题在这里已经有答案了 有没有办法从Uri to URL 我正在使用的库需要这个 它only接受一个URL但我需要在我的设备上使用图像 如果该方案的Uri is http or https new URL uri toString 应该
  • 导航抽屉默认片段

    我是一名新手开发人员 我正在将导航抽屉与 android support v7 集成到我的应用程序中 我有一个问题 当我启动应用程序时 主要布局是这样的
  • 如何在进入新活动之前终止线程和处理程序

    大家好 在我尝试清理处理程序时 这段代码可能有点混乱 因为我一直在尝试追踪崩溃发生的位置 我有一个对话框活动 显示密码输入 进度条由线程和处理程序动画显示 似乎当我试图查看进度条是否完成并尝试终止线程时 当我尝试进入新活动时 我这样做的方式
  • 如何以编程方式启动 ssh 服务器 android,以及如何获取连接到设备的用户名和密码

    我正在开发像这样的应用程序sshdroid 我想在 Android 操作系统上打开 ssh 连接 并且我想从电脑连接应用程序 我使用了 JSCH lib 但是这个lib用于将android连接到pc 我的要求是pc到android 任何人都
  • 为什么Android应用程序在发布到市场后尺寸会增加?

    我最近在 Android 市场上发布了我的应用程序 显示应用程序大小为 5 4MB 而实际 apk 大小为 2 8MB 为什么显示多出2MB 我应该如何限制我的应用程序大小 请帮我 您的应用程序大小会增加 因为您使用了复制保护选项ON在发布
  • Android 从命令行停止模拟器

    这个问题与如何通过命令行关闭Android模拟器 https stackoverflow com questions 5912403 how to shut down android emulator via cmd 但是 在尝试第一个答案
  • 使用函数的 SQL 查询 - 如何获取列表的最大计数

    如何查询 MAXIMUM COUNT 交易次数 我的代码如下 SELECT customer id COUNT customer id FROM rental GROUP BY customer id HAVING MAX COUNT cu
  • 选择获取与 MySQL Group 中 max 对应的整行

    当我使用Max使用后查找特定 MySQL 组中字段的最大值GROUP BY 是否可以获取包含最大值的整行 我在处理一些论坛代码时偶然发现了这个线程 我想获取每个线程的最新帖子并将其显示在特定板的线程列表中 Quassnoi上面的回答对我非常
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • 在 Honeycomb Android 3.0 中显示 Action Bar 菜单项的图标

    我正在使用 Honeycomb android 3 0 开发 Android 应用程序 我正在尝试在 Action Bar 中显示菜单 菜单有一个图标和标题 当我们单击菜单项时 它会以下拉列表的形式显示其项目 它是下拉列表中带有项目名称但不
  • Android - 9 补丁

    我正在尝试使用 9 块图片创建一个新的微调器背景 我尝试了很多方法来获得完美的图像 但都失败了 s Here is my 9 patch 当我用Draw 9 patch模拟时 内容看起来不错 但是带有箭头的部分没有显示 或者当它显示时 这部
  • 在android中跟踪FTP上传数据?

    我有一个运行 Android 的 FTP 系统 但我希望能够在上传时跟踪字节 这样我就可以在上传过程中更新进度条 安卓可以实现这个功能吗 现在 我正在使用org apache common net ftp我正在使用的代码如下 另外 我在 A
  • 在没有 Wifi 的情况下获取 Android 设备的 MAC 地址

    如何获取没有 Wifi 接口的 Android 设备 例如 Android 模拟器 的网络接口的 MAC 地址 通过WifiManager返回获取的WifiInfonull EDIT 更清楚地说 我必须与本地网络上的现有网络协议 不是我设计

随机推荐