当前位置: 首页 > article >正文

MySQL索引优化实战一

#插入一些示例数据
 drop procedure if exists insert_emp;
 delimiter ;;
 create procedure insert_emp()
 begin
 declare i int;
 set i=1;
 while(i<=100000)do
 insert into employees(name,age,position) values(CONCAT('tqq',i),i,'dev');
 set i=i+1;
 end while;
 end;;
 delimiter ;
 call insert_emp();
 
 EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

1、联合索引第一个字段用范围不会走索引

 EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述
结论:联合索引第一个字段就用返回查找不会走索引,MySQL内部可能觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不如走全表扫描。

2、强制走索引

 EXPLAIN SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述
结论:虽然使用了强制走索引让联合索引第一个字段范围查找也走索引,扫描的行rows看上去也少了点,但是最终查找效率不一定比全表扫描高,因为回表效率不高

 ‐‐ 关闭查询缓存
 set global query_cache_size=0;
 set global query_cache_type=0;
 ‐‐ 执行时间0.333s
 SELECT * FROM employees WHERE name > 'LiLei';
 ‐‐ 执行时间0.444s
 SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei';

3、覆盖索引优化

查询结果集符合最左前缀原则,减少回表。

 EXPLAIN SELECT name,age,position FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述

4、in和or在表数据量比较大的时候会走索引,在表记录不多的情况下会选择全表扫描

 EXPLAIN SELECT * FROM employees WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

在这里插入图片描述

 EXPLAIN SELECT * FROM employees WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

在这里插入图片描述
做一个小实验,将employees 表复制一张employees_copy的表,里面保留两三条记录

 
 EXPLAIN SELECT * FROM employees_copy WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';
 
 EXPLAIN SELECT * FROM employees_copy WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

在这里插入图片描述
在这里插入图片描述

5、 like KK% 一般情况都会走索引

  EXPLAIN SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

在这里插入图片描述

	EXPLAIN SELECT * FROM employees_copy WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

在这里插入图片描述

索引下推(Index Condition Pushdown,ICP)

like KK%其实就是用到了索引下推优化
对于辅助的联合索引(name,age,position),正常情况按照最左前缀原则,SELECT * FROM employees WHERE name like ‘LiLei%’ AND age = 22 AND position =‘manager’ 这种情况只会走name字段索引,因为根据name字段过滤完,得到的索引行里的 age 和 position是无序的,无法很好的利用索引。在MySQL5.6之前的版本,这个查询只能在联合索引里匹配到名字是 ‘LiLei’ 开头的索引,然后拿这些索引对应的主键逐个回表,到主键索引上找出相应的记录,再比对age和position这两个字段的值是否符合。MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。使用了索引下推优化后,上面那个查询在联合索引里匹配到名字是 ‘LiLei’ 开头的索引之后,同时还会在索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。
索引下推会减少回表次数,对于innodb引擎的表索引下推只能用于二级索引,innodb的主键索引(聚簇索引)树叶子节点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果。

为什么范围查找MySQL没有用索引下推优化?

估计应该是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里MySQL选择给 like KK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。

MySQL如何选择合适的索引

EXPLAIN select * from employees where name > 'a';

没走索引
在这里插入图片描述

EXPLAIN select * from employees where name > 'zzz';
 

走了索引
在这里插入图片描述
MySQL会计算走索引和不走索引的成本,如果走索引会看走那个索引花费的成本小。

trace工具用法

set session optimizer_trace="enabled=on",end_markers_in_json=on; ‐‐开启trace
select * from employees where name > 'a' order by position;
SELECT * FROM information_schema.OPTIMIZER_TRACE;

常见sql深入优化

order by 与group by 优化
在这里插入图片描述
上图用了索引,如果Extra中用了Using filesort就是没有用索引

case 8:
数据量大所以走了全表扫描。
在这里插入图片描述
可以使用覆盖索引优化:
在这里插入图片描述

优化总结:

1、MySQL支持两种方式排序:indexfilesort,Using index MySQL扫描索引本身完成排序,效率高,filesort效率低。
2、order by 会使用Using index的两种情况:

  • order by 使用索引最左前列
  • 使用where 子句与order by 子句条件组合满足索引最左前列
    3、尽量在索引类上完成排序,遵循索引建立时的最左前缀法则;
    4、如果order by的条件不在索引列上,就会产生 Using filesort
    5、能用覆盖索引尽量用覆盖索引
    6、group by 与 order by 很类似,实质就是先排序后分组。遵循索引创建最左前缀法则,对于group by 的优化如果不需要排序的加上order by null禁止排序。where高于having,能写在where中的限定条件不要在having中限定。

Using filesort文件排序原理详解

filesort文件排序方式【单路排序、双路排序(回表)】

  • 单路排序
    一次性取出所有的满足条件行的字段,在sort buffer中排序,trace工具可以看到sort_mode信息里显示<sort_key,additional_fields> 或则 <sort_key,packed_additional_fields>
  • 双路排序(回表)
    首先根据相应的条件取出相应的排序字段和可以直接定位行数据的ID,然后在sort suffer中进行排序,排序完再通过ID回表取出其他需要的字段。trace工具可以看到sort_mode信息里显示<sort_key,rowid>

MySQL通过比较系统变量max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来判断使用那种模式:

  • 如果字段的总长度小于max_length_for_sort_data,那么使用单路排序;
  • 如果字段的总长度大于max_length_for_sort_data,那么使用双路排序。
explain select * from employee where name = ‘tqq’ order by  position

单路排序详细过程:
1、从索引name找到第一个满足name=‘tqq’ 条件的主键id;
2、根据主键id取出整行,取出所有字段的值,存入sort Buffer中
3、从索引name找到下一个满足name = 'tqq’条件的主键id;
4、重复步骤2、3直到不满足name=‘tqq’
5、对sort buffer 中的数据按照字段position进行排序
6、返回结果给客户端

双路排序详细过程:
1、从索引name找到第一个满足name=‘tqq’ 条件的主键id;
2、根据主键id取出整行,把排序字段position和主键id两个字段放到sort buffer中
3、从索引name取出下一个满足name = 'tqq’记录的主键
4、重复步骤2、3直到不满足name=‘tqq’
5、对sort buffer中的字段position和主键id按照字段position进行排序
6、遍历排序好的id和字段position,按照id的值回到原表中取出所有字段返回给客户端

索引的设计原则

1、代码先行,索引后上
等业务主体功能开发完毕,把设计到该表相关的SQL拿出来分析之后再建立索引。
2、联合索引尽量覆盖条件
尽量少建单值索引。可以设计1-3个联合索引,让每一个联合索引都尽量去包含SQL语句里的where、order by 、group by 的字段,联合索引的字段顺序尽量满足SQL查询的最左前缀原则。
3、不要在小基数字段上建立索引
索引基数就是这个字段在表中总共有多少个不同的值。比如性别。
4、长字符串我们可以采用前缀索引
对于varchar(255)的大字段可能会比较占用磁盘空间,可以对字段的前20个字符建立索引,对这个字段里的每个值的前20个字符放在索引树中,类似key index(name(20),age,position),这样就不能使用order by 了
5、where与order by 冲突的时候优先where
一般这种时候往往都是让where条件去使用索引快速搜索出来一部分指定的数据,接着再进行排序。因为大多数情况基于索引进行where筛选往往可以最快速读筛选出你想要的数据,然后排序的成本可能会小很多
6、基于慢SQL查询做优化
可以根据监控后台的一些慢SQL,针对慢SQL查询做特定的索引优化
慢SQL查询:https://blog.csdn.net/qq_40884473/article/details/89455740

设计索引的核心思想是:尽量利用一两个复杂的多字段联合索引,抗下80%以上的查询,然后用一两个辅助索引尽量抗下剩余的一些非典型查询。保证大数据量表的查询尽可能多的都能充分利用索引,这样就能保证查询速读和性能了。


http://www.kler.cn/a/149041.html

相关文章:

  • 闯关leetcode——3174. Clear Digits
  • 安全生产管理的重要性:现状、痛点与改进之路
  • 区块链技术在电子政务中的应用
  • 【贪心算法】No.1---贪心算法(1)
  • 有什么初学算法的书籍推荐?
  • XML Schema 字符串数据类型
  • 万户协同办公平台ezoffice SendFileCheckTemplateEdit.jsp接口存在SQL注入漏洞 附POC
  • SpringBoot项目整合Redis,Rabbitmq发送、消费、存储邮件
  • 代码随想录算法训练营 ---第四十九天
  • 在Visual Studio Code中安装加速TypeScript程序开发的插件
  • Git工作流和Commit规范
  • 12:kotlin类和对象 -- 继承
  • C语言第三十六弹--实现转移表的多种方法
  • redis基本数据结构
  • FFA 2023|字节跳动 7 项议题入选
  • 计算机基础知识57
  • Ubuntu 上使能 SELinux
  • 王者小游戏
  • 【好用的个人工具】在Docker环境下部署Simple mind map思维导图工具
  • 云原生系列Go语言篇-泛型Part 2
  • 【Python】jieba分词基础
  • 【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“”取地址运算符重载)
  • python与机器学习1,机器学习的一些基础知识(完善ing)
  • Prometheus的详细部署
  • spring boot spring-retry重试机制
  • 算法 离散化