Mysql的索引优化原则详解

 更新时间:2025年12月16日 10:43:53   作者:小吴学不废Java  
本文详细介绍了MySQL索引优化原则,包括最佳左前缀法则、不要在索引列上做任何计算、避免使用范围条件、避免使用特定运算符等,通过这些原则,可以提高查询效率,避免全表扫描,感兴趣的朋友跟随小编一起看看吧
  • 创建数据库、表,插入数据
create database idx_optimize character set 'utf8';
CREATE TABLE users(
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_name VARCHAR(20) NOT NULL COMMENT '姓名',
  user_age INT NOT NULL DEFAULT 0 COMMENT '年龄',
  user_level VARCHAR(20) NOT NULL COMMENT '用户等级',
  reg_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
);
INSERT INTO users(user_name,user_age,user_level,reg_time)
VALUES('tom',17,'A',NOW()),('jack',18,'B',NOW()),('lucy',18,'C',NOW());
  • 创建联合索引
ALTER TABLE users ADD INDEX idx_nal (user_name,user_age,user_level) USING BTREE;

2.优化原则详解

1)最佳左前缀法则

最佳左前缀法则: 如果创建的是联合索引,就必须遵守这个法则,当使用联合索引时,where后面条件需要从索引的最左前开始使用。

  • 场景1: 按照索引字段顺序使用,三个字段都使用了索引,没有问题
EXPLAIN SELECT * FROM users WHERE user_name = 'tom' 
AND user_age = 17 AND user_level = 'A';

  • 场景2: 直接跳过user_name使用索引字段,索引无效,未使用到索引。
EXPLAIN SELECT * FROM users WHERE user_age = 17 AND user_level = 'A';

  • 场景3: 不按照创建联合索引的顺序,使用索引
EXPLAIN SELECT * FROM users WHERE 
user_age = 17 AND user_name = 'tom' AND user_level = 'A';

where后面查询条件顺序是 user_ageuser_leveluser_name与我们创建的索引顺序user_nameuser_ageuser_level不一致,为什么还是使用了索引,原因是因为MySql底层优化器对其进行了优化。

  • 最佳左前缀底层原理
  1. MySQL创建联合索引的时候要遵守一个规则: 首先会对联合索引最左边的字段进行排序,再在第一个字段的基础之上对第二个字段进行排序。

所以: 最佳左前缀原则其实是和B+树的结构有关系, 最左字段肯定是有序的, 第二个字段则是无序的(联合索引的排序方式是: 先按照第一个字段进行排序,如果第一个字段相等再根据第二个字段排序). 所以如果直接使用第二个字段 user_age 通常是使用不到索引的.

2) 不要在索引列上做任何计算

不要在索引列上做任何操作,比如计算、使用函数、自动或手动进行类型转换,会导致索引失效,从而使查询转向全表扫描。

  • 插入数据
INSERT INTO users(user_name,user_age,user_level,reg_time) VALUES('11223344',22,'D',NOW());
  • 场景1: 使用系统函数 left()函数,对user_name进行操作
EXPLAIN SELECT * FROM users WHERE LEFT(user_name, 6) = '112233';

场景2: 字符串不加单引号 (隐式类型转换)

varchar类型的字段,在查询的时候不加单引号,就需要进行隐式转换, 导致索引失效,转向全表扫描。

EXPLAIN SELECT * FROM users WHERE 

3) 范围之后全失效

范围之后全失效: where条件中如果有范围条件,并且范围条件之后还有其他条件.

  • 场景1: 条件单独使用user_name时, type=ref, key_len=62
-- 条件只有一个 user_name
EXPLAIN SELECT * FROM users WHERE user_name = 'tom';

场景2: 条件增加一个 user_age ( 使用常量等值) ,type= ref , key_len = 66

EXPLAIN SELECT * FROM users WHERE user_name = 'tom' AND user_age = 17;

场景3: 使用全值匹配, type = ref , key_len = 128 , 索引都利用上了.

EXPLAIN SELECT * FROM users WHERE user_name = 'tom' 
AND user_age = 17 AND user_level = 'A';

场景4: 使用范围条件时, avg > 17 , type = range , key_len = 66 , 与场景3 比较,可以发现 user_level 索引没有用上.

-----使用范围条件  user_age>17  ,user_level索引就失效了
EXPLAIN SELECT * FROM users WHERE user_name = 'tom' 
AND user_age > 17 AND user_level = 'A';

4) 避免使用 is null 、 is not null、!= 、or

  • 使用 is null 会使索引失效
EXPLAIN SELECT * FROM users WHERE user_name IS NULL;
---Impossible where: 表示where条件不成立,不能返回任何的行

  • 使用 is not null 会使索引失效
EXPLAIN SELECT * FROM users WHERE user_name IS NOT NULL;  ---全表扫描

  • 使用 !=or 会使索引失效
EXPLAIN SELECT * FROM users WHERE user_name != 'tom';
EXPLAIN SELECT * FROM users WHERE user_name = 'tom' or user_name = 'jack';

5) like以%开头会使索引失效

like查询为范围查询,%出现在左边,则索引失效。%出现在右边索引未失效.

  • 场景1: 两边都有% 或者 字段右边有%,索引都会失效
EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom%';
EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom';

对比场景1可以知道, 通过使用覆盖索引 type = index,并且 extra = Using index,从全表扫描变成了全索引扫描.

场景2: 字段左边有%,索引生效

EXPLAIN SELECT * FROM users WHERE user_name LIKE 'tom%';

解决%出现在左边索引失效的方法

  • 使用覆盖索引
EXPLAIN SELECT user_name FROM users WHERE user_name LIKE '%jack%';
EXPLAIN SELECT user_name,user_age,user_level FROM users WHERE user_name LIKE '%jack%';

like 失效的原理

  1. %号在右: 由于B+树的索引顺序,是按照首字母的大小进行排序,%号在右的匹配又是匹配首字母。所以可以在B+树上进行有序的查找,查找首字母符合要求的数据。所以有些时候可以用到索引.
  2. %号在左: 是匹配字符串尾部的数据,我们上面说了排序规则,尾部的字母是没有顺序的,所以不能按照索引顺序查询,就用不到索引.
  3. 两个%%号: 这个是查询任意位置的字母满足条件即可,只有首字母是进行索引排序的,其他位置的字母都是相对无序的,所以查找任意位置的字母是用不上索引的.

索引优化原则总结

  • 最左前缀法则要遵守
  • 索引列上不计算
  • 范围之后全失效
  • 覆盖索引记住用。
  • 不等于、is null、is not null、or导致索引失效。
  • like百分号加右边,加左边导致索引失效,解决方法:使用覆盖索引。

到此这篇关于Mysql的索引优化原则的文章就介绍到这了,更多相关mysql索引优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql错误码1045解决方案

    mysql错误码1045解决方案

    当使用数据库连接工具访问MySQL时,可能会出现错误码1045,导致无法登录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-10-10
  • MySQL插入时间戳字段的值实现

    MySQL插入时间戳字段的值实现

    在MySQL中,我们经常会遇到需要插入时间戳字段的情况,包括使用NOW()函数插入当前时间戳,使用FROM_UNIXTIME()插入指定时间戳,本文就来介绍一下,感兴趣的可以了解一下
    2024-09-09
  • MySQL数据库之数据表操作DDL数据定义语言

    MySQL数据库之数据表操作DDL数据定义语言

    这篇文章主要介绍了MySQL数据库之数据表操作DDL数据定义语言,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • mysql 常用命令集锦[绝对精华]

    mysql 常用命令集锦[绝对精华]

    测试环境:mysql 5.0.45 【注:可以在mysql中通过mysql> SELECT VERSION();来查看数据库版本】
    2009-06-06
  • MySQL数据库配置信息查看与修改方法详解

    MySQL数据库配置信息查看与修改方法详解

    我们通常把在项目中使用的常量收集在一个文件,这个文件就是配置文件,下面这篇文章主要给大家介绍了关于MySQL数据库配置信息查看与修改的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • MySQL 8.0用户和角色管理原理与用法详解

    MySQL 8.0用户和角色管理原理与用法详解

    这篇文章主要介绍了MySQL 8.0用户和角色管理,结合实例形式分析了MySQL 8.0用户和角色管理相关概念、功能、原理、用法及操作注意事项,需要的朋友可以参考下
    2020-04-04
  • mysql获取当前日期年月的两种实现方式

    mysql获取当前日期年月的两种实现方式

    这篇文章主要介绍了mysql获取当前日期年月的两种实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • MySQL创建用户与授权及撤销用户权限方法

    MySQL创建用户与授权及撤销用户权限方法

    这篇文章主要介绍了MySQL创建用户并授权及撤销用户权限、设置与更改用户密码、删除用户等等,需要的朋友可以参考下
    2014-08-08
  • 远程连接mysql数据库注意事项记录(远程连接慢skip-name-resolve)

    远程连接mysql数据库注意事项记录(远程连接慢skip-name-resolve)

    有时候我们需要远程连接mysql数据库,就需要注意下面的问题,方便大家解决,脚本之家小编特为大家准备了一些资料
    2012-07-07
  • MySQL数据库手册DATABASE操作与编码(小白入门篇)

    MySQL数据库手册DATABASE操作与编码(小白入门篇)

    这篇文章主要介绍了MySQL数据库手册DATABASE操作与编码的小白入门篇,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05

最新评论