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

也许大多数人对索引优化的理解就是调优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下执行时不会走索引。

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

如果喜欢本内容的话,关注公众号查看更多内容,微信搜索:京城小人物,或者扫描下方二维码,动动小手给作者一个鼓励,比心!

相关推荐
lUie INGA2 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
极客on之路2 小时前
mysql explain type 各个字段解释
数据库·mysql
代码雕刻家2 小时前
MySQL与SQL Server的基本指令
数据库·mysql·sqlserver
lThE ANDE2 小时前
开启mysql的binlog日志
数据库·mysql
geBR OTTE2 小时前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
Porunarufu2 小时前
博客系统UI自动化测试报告
java
NineData2 小时前
NineData 新增支持 GaussDB 到 StarRocks 实时数据复制能力
后端
sghuter3 小时前
数字资源分发架构解密
后端·架构·dubbo
小码哥_常3 小时前
Spring Boot启动慢?这5个优化点带你起飞
后端