如何优化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下执行时不会走索引。

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

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

相关推荐
钢门狂鸭2 小时前
关于rust的crates.io
开发语言·后端·rust
Lionel_SSL3 小时前
《深入理解Java虚拟机》第三章读书笔记:垃圾回收机制与内存管理
java·开发语言·jvm
记得开心一点嘛3 小时前
手搓Springboot
java·spring boot·spring
老华带你飞3 小时前
租房平台|租房管理平台小程序系统|基于java的租房系统 设计与实现(源码+数据库+文档)
java·数据库·小程序·vue·论文·毕设·租房系统管理平台
独行soc3 小时前
2025年渗透测试面试题总结-66(题目+回答)
java·网络·python·安全·web安全·adb·渗透测试
脑子慢且灵4 小时前
[JavaWeb]模拟一个简易的Tomcat服务(Servlet注解)
java·后端·servlet·tomcat·intellij-idea·web
华仔啊5 小时前
SpringBoot 中 6 种数据脱敏方案,第 5 种太强了,支持深度递归!
java·后端
异常驯兽师6 小时前
Spring 中处理 HTTP 请求参数注解全解析
java·spring·http
F_D_Z6 小时前
【SQL】指定日期的产品价格
数据库·sql·mysql
程序员在线炒粉8元1份顺丰包邮送可乐6 小时前
Docker 部署生产环境可用的 MySQL 主从架构
mysql·docker·架构