文章目录:

1.案例分析

1.1 数据准备

1.2 全值匹配

1.3 最左前缀法则

1.4 计算、函数、类型转换(自动或手动)导致索引失效

1.5 范围条件右边的列索引失效

1.6 不等于(!= 或者<>)索引失效

1.7 is null可以使用索引,is not null无法使用索引

1.8 like以通配符%开头索引失效

1.9 OR前后存在非索引的列,索引失效

1.10 数据库和表的字符集统一使用utf8mb4

2.结束语


1.案例分析

1.1 数据准备

这里主要用到的是 class、student 这两张表。

CREATE DATABASE atguigudb2;USE atguigudb2;#建表
CREATE TABLE `class` (`id` INT(11) NOT NULL AUTO_INCREMENT,`className` VARCHAR(30) DEFAULT NULL,`address` VARCHAR(40) DEFAULT NULL,`monitor` INT NULL ,PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE TABLE `student` (`id` INT(11) NOT NULL AUTO_INCREMENT,`stuno` INT NOT NULL ,`name` VARCHAR(20) DEFAULT NULL,`age` INT(3) DEFAULT NULL,`classId` INT(11) DEFAULT NULL,PRIMARY KEY (`id`)#CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;SET GLOBAL log_bin_trust_function_creators=1; #随机产生字符串
DELIMITER //
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;#用于随机产生多少到多少的编号
DELIMITER //
CREATE FUNCTION  rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+1))   ;
RETURN i;
END //
DELIMITER ;#创建往stu表中插入数据的存储过程
DELIMITER //
CREATE PROCEDURE  insert_stu(  START INT ,  max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;   SET autocommit = 0;    #设置手动提交事务REPEAT  #循环SET i = i + 1;  #赋值INSERT INTO student (stuno, NAME ,age ,classId ) VALUES ((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000));  UNTIL i = max_num  END REPEAT;  COMMIT;  #提交事务
END //
DELIMITER ;#执行存储过程,往class表添加随机数据
DELIMITER //
CREATE PROCEDURE insert_class( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;   SET autocommit = 0;    REPEAT  SET i = i + 1;  INSERT INTO class ( classname,address,monitor ) VALUES (rand_string(8),rand_string(10),rand_num(1,100000));  UNTIL i = max_num  END REPEAT;  COMMIT;
END //
DELIMITER ;#执行存储过程,往class表添加1万条数据
CALL insert_class(10000);#执行存储过程,往stu表添加50万条数据
CALL insert_stu(100000,500000);SELECT COUNT(*) FROM class;SELECT COUNT(*) FROM student;
#首先确保student表中只有一个主键对应的索引
SHOW INDEX FROM student;

1.2 全值匹配

我们先对age字段单独建一个索引,执行下面这三条sql,它们都会走这个索引。

CREATE INDEX idx_age ON student(age);EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

CREATE INDEX idx_age_classid ON student(age,classId);EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

1.3 最左前缀法则

这里肯定会用到我们上面创建的age单列索引。

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abcd' ;

下面这个,必然用不上我们上面创建的那三个索引的,因为联合索引中是先依赖 age、classId、NAME 建立的B+树。

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid=1 AND student.name = 'abcd';

这里的三个字段对应了联合索引中的三个字段,这里是可以交换位置的。

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE classid=4 AND student.age=30 AND student.name = 'abcd'; 

接下来,我们删除两个索引,留下三个字段的那个联合索引,看看效果。

由于先走age,age相同再走classId,这里没有classId了话,那么就直接去走NAME了,所以可以用上这个联合索引的。但是后面的key_len为5,表示只用上了联合索引中的age这部分(INT类型4个字节 + 非空NULL1个字节 = 5个字节),并没有用到NAME。

DROP INDEX idx_age ON student;
DROP INDEX idx_age_classid ON student;EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age=30 AND student.name = 'abcd'; 

1.4 计算、函数、类型转换(自动或手动)导致索引失效

先针对NAME字段建一个单列索引,以便下面测试。

CREATE INDEX idx_name ON student(NAME);
#此语句比下一条要好!(能够使用上索引)
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc'; 

这里不会走索引,其实是因为用了LEFT函数之后,我又不知道name在经过LEFT函数之后是什么样的,还怎么走B+树呢?

下面,我们再针对stuno建一个索引。 (计算也会导致索引失效)

CREATE INDEX idx_sno ON student(stuno);
EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno = 900000;

EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;

下面演示一下  类型转换  导致索引失效的问题。

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME = 123; 

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME = '123'; 

1.5 范围条件右边的列索引失效

首先将多余的索引删除,只保留主键对应的那个索引。

DROP INDEX idx_sno ON student;
DROP INDEX idx_name ON student;
DROP INDEX idx_age_classid_name ON student;
SHOW INDEX FROM student;

CREATE INDEX idx_age_classId_name ON student(age,classId,NAME);EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age=30 AND student.classId>20 AND student.name = 'abc' ; 

这里的确是可以用到上面创建好的这个索引,但是它的key_len=10,原因就是:age(INT 4个字节 + 非空 1个字节)=5个字节,classId和age一样,此时就是 5 + 5 = 10个字节,那么前两个都用到了,为什么联合索引的第三个NAME没有用到呢?这是因为classId这里是一个范围条件,在它右侧的那些字段将不会使用索引。

那么如果还是上面这个问题,我们想用到联合索引的所有字段,应该怎么优化呢?

MySQL高级篇——索引失效案例相关推荐

  1. MYSQL高级篇-----索引优化分析

    索引优化分析 下面是目录可跳转对应页面学习: 2. 索引优化分析 2.1 原因 SQL执行顺序 2.2 常见通用的join查询 2.3 索引 2.3.1 索引分类(重点) 2.3.2 索引结构 2.3 ...

  2. MySQL高级or索引失效情况

    用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到. 示例,name字段是索引列 , 而createtime不是索引列,中间是or进行连接是不走索引的 ...

  3. MySQL高级篇——索引简介

  4. mysql高级篇(二)mysql索引优化分析

    mysql高级篇笔记 mysql高级篇(一)mysql的安装配置.架构介绍及SQL语句的复习. mysql高级篇(二)mysql索引优化分析. mysql高级篇(三)查询截取分析(慢查询日志).主从复 ...

  5. MySQL高级篇知识点——索引优化与查询优化

    目录 1.数据准备 1.1.建库建表 1.2.创建相关函数 1.3.创建存储过程 1.4.调用存储过程 1.5.删除某表上的索引 2.索引失效案例 2.1.全值匹配 2.2.最佳左前缀匹配原则 2.3 ...

  6. MySQL高级篇(SQL优化、索引优化、锁机制、主从复制)

    目录 0 存储引擎介绍 1 SQL性能分析 2 常见通用的JOIN查询 SQL执行加载顺序 七种JOIN写法 3 索引介绍 3.1 索引是什么 3.2 索引优劣势 3.3 索引分类和建索引命令语句 3 ...

  7. 《MySQL高级篇》八、索引优化与查询优化

    文章目录 1. 数据准备 2. 索引失效案例 2.1 全值匹配我最爱 2.2 最左匹配原则 2.3 主键插入顺序 2.4 计算.函数.类型转换(自动或手动)导致索引失效 2.5 范围条件右边的列索引失 ...

  8. MySQL高级 —— 高性能索引

    引言 最近一直在抱着<高性能MySQL(第三版)>研究MySQL相关热点问题,诸如索引.查询优化等,这阶段的学习是前一段时间MySQL基础与官方的"阅读理解"的进一步延 ...

  9. mysql高级篇学习笔记

    目录 前言 1 mysql安装及运行(linux环境) 1.1 安装前检查 1.2 MySQL卸载 ①**关闭 mysql 服务** ②**查看当前 mysql 安装状况** ③**卸载上述命令查询出 ...

最新文章

  1. 谷歌翻译无法连接网络_Windows无法连接网络,这几招教你解决
  2. 计算机汉字编码贵州,计算机汉字输入编码方法
  3. 工作中关于rpm的一个简单但头疼的问题
  4. ARM架构中MMU/TLB/Cache的一些概念和寄存器
  5. 阿里云移动端播放器高级功能---截图和音频波形
  6. 数据库开发——MySQL——慢查询优化
  7. 查看数据库系统字符集
  8. HDU 1253 胜利大逃亡 题解
  9. java完全解耦_java-完全解耦 - osc_bc7dotjc的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. iOS开发-多层嵌套block中如何使用__weak和__strong
  11. 客户端无法远程连接服务器的问题
  12. python 组态_西门子组态WinCC自学入门视频教程资源_48讲
  13. java并发增强工具_0318 guava并发工具
  14. Maven使用tomcat8-maven-plugin插件
  15. linux qt 找不到 lgl,Linux Qt cannot find -lGL错误完美解决方案(亲测有效)
  16. 鹿晓亮:基于大数据云计算的语音识别深度平台
  17. 小米15.6笔记本安装UBUNTU18.04 无WIFI驱动解决方法
  18. python 应用程序无法正常启动 000007b_为你解答应用程序无法正常启动00xc000007b怎么办...
  19. 凸优化笔记(一):仿射集,凸集与锥
  20. PageHelper 插件踩过的坑

热门文章

  1. 【枚举】讨厌的青蛙,总踩我的稻田:( 谁最可恨?(POJ百练2812)
  2. C语言 记忆测试(Memory Test)小游戏的实现
  3. 台式机Ubuntu18.04 装无限网卡驱动问题记录
  4. 资产负债表比率的计算
  5. 【聚类学习】时间序列聚类—10年回顾概括性综述
  6. Blog of Friends
  7. .NET平台下的仪表盘组件Chart FX Gauges
  8. office VBA 用户窗体 控件 单元格 参数的用法与注解
  9. DataGridView查询数据
  10. 用CA实现通过https(或443端口)访问网站