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

354 阅读5分钟

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

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

最左前缀原则

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

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

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

假设我们现在有一张student表

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

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

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

SQL2

EXPLAIN SELECT * FROM students WHERE age = 18;

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

SQL3

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

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

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

深入分析

SQL4

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

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

SQL5

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

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

SQL6

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

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

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

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

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

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

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

SQL8

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

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

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

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