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

MySQL的Sql优化经验总结

参考资料:

参考视频1:【MySQL】8分钟掌握MySQL万能优化法

参考视频2:SQL优化(MySQL版;不适合初学者,需有数据库基础)

参考文章:为什么会使用内部临时表

参考文章:group by 的优化

参考文章:sql查询调优之where条件排序字段以及limit使用索引的奥秘

参考文章:Mysql-explain之Using temporary和Using filesort解决方案

参考文章:mysql数据库多表关联查询的慢SQL优化

参考文章:MySQL高级知识(八)——ORDER BY优化

参考文章:group by 优化大法

参考文章:MySQL 优化学习笔记 - 简书

参考文章:轻松优化MySQL-之Join、group by语句的优化 - 简书

参考文章:大能猫

参考文章:【MySQL】MySQL性能优化之Block Nested-Loop Join(BNL)_ITPUB博客

参考文章:https://zhuanlan.zhihu.com/p/144289721

参考文章:https://blog.csdn.net/weixin_43852196/article/details/117694947

参考文章:MySQL 处理大数据表的 3 种方案,建议收藏!!


概述:

        想真的提高运行效率,可以试着分区,分库分表、冷热隔离,优化只是暂时提高效率


优化案例:

总体纲领

全值匹配我最爱,最左前缀要遵守;

带头大哥不能死,中间兄弟不能断;

索引列上少计算,范围之后全失效;

LIKE百分写最右,覆盖索引不写*;

不等空值还有OR,索引影响要注意;

varchar引号不可丢, SQL优化有诀窍

SQL解析顺序

编写过程:

select dinstinct  ..from  ..join ..on ..where ..group by ...having ..order by ..limit ..

解析过程

from .. on.. join ..where ..group by ....having ...select dinstinct ..order by limit ...

优化等级

Using index

直接在索引中查,不需要回表,性能最佳

Using where

需要回表查询,浪费性能

Using filesort

多余的查询,浪费性能  一般出现在  where 和  order by  不按照最左前缀原则使用复合索引

using temporary

性能损耗大 ,用到了临时表。一般出现在group by 语句中

impossible where

where子句永远为false

using join buffer

Mysql引擎使用了 连接缓存,大白话就是sql性能太差了,mysql  不得不  给这个sql加一个缓存  ,所以这个是需要优化的。

优化案例1

create table test03
(a1 int(4) not null,a2 int(4) not null,a3 int(4) not null,a4 int(4) not null
);
alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a3=3 and a4 =4 ;

--推荐写法,因为 索引的使用顺序(where后面的顺序) 和 复合索引的顺序一致

explain select a1,a2,a3,a4 from test03 where a4=1 and a3=2 and a2=3 and a1 =4 ; 

--虽然编写的顺序 和索引顺序不一致,但是 sql在真正执行前 经过了SQL优化器的调整,结果与上条SQL是一致的。

--以上 2个SQL,使用了 全部的复合索引

以上这两个sql效果一样   ,且两项重要的指标也一致

explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3; 

        --以上SQL用到了a1 a2两个索引,该两个字段 不需要回表查询using index ;而a4因为跨列使用,造成了该索引失效,需要回表查询 因此是using where;以上可以通过 key_len进行验证

        然后这个sql只有两个字段生效  即a1,a2,并且回表查询,所以出现了using  where,a3,a4索引失效,且order by  a3  和前面生效的 a1,a2拼接起来满足索引顺序,所以a3不会造成using filesort

explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a3; 

        --以上SQL出现了 using filesort(文件内排序,“多了一次额外的查找/排序”) :不要跨列使用( where和order by 拼起来,不要跨列使用)

        因为order by  a3  和前面生效的索引a1连起来,并没有满足复合索引的顺序  ,所以  a1造成的  using index  ,a3,a4造成的using where   回表查询 , a3是因为where 和  order by  连起来没有遵循最左前缀原则,造成的  using filesort  额外查询。

        如下,首先  a1是个生效的索引,所以using index  ,又  a1和a3并没有遵循最左前缀原则,所以造成using filesot,又需要按照a3来排序,所以 造成回表查询  using where

	explain select a1,a2,a3,a4 from test03 where a1=1 order by a3;

explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a2 , a3;

        --不会using filesort

        因为where  中  生效的 a1  索引,,order by 生效的是a2,a3索引,符合  最左前缀原则,所以没有using filesorft  又  a4以及  a2,a3需要回表重新查询 所以  using where

        注意: where  和  order by  也不应该违背最左前缀原则

优化案例2-单表优化

create table book
(bid int(4) primary key,name varchar(20) not null,authorid int(4) not null,publicid int(4) not null,typeid int(4) not null 
);insert into book values(1,'tjava',1,1,2) ;
insert into book values(2,'tc',2,1,2) ;
insert into book values(3,'wx',3,2,1) ;
insert into book values(4,'math',4,2,3) ;	
commit;	可以看到,索引是主键  bid

查询authorid=1且 typeid为2或3的 bid

explain select bid from book where typeid in(2,3) and authorid=1  order by typeid desc ;

        可以看到,这条语句是很糟糕的,type  为all这是性能最差的类型,然后key和possible key都为空,并且  using where  和 using  filesort都出现了

根据

根据SQL实际解析的顺序,调整索引的顺序:

alter table book add index idx_tab (typeid,authorid,bid);

        --将bid放到索引中 可以提升使用using index ,如果不加  ,还需要  回表查询  ,变为  using where

添加上述索引后  然后再执行  ,变为

        可以看到,这次优化,将  type提升为  index  ,生成了key  和possible  key  ,然后  extra  提升为  using where  和  using  index

        

        注意   索引一旦进行升级优化  需要将原先的索引删掉,防止干扰

        但是这个优化还是不是很彻底 ,标准的是  type  ref>range  为最优  ,而且  extra  还有多余的using where

        又  in  所在的字段  很容易使该字段的索引失效  ,所以(typeid,authorid,bid)这类索引,一旦  typeid失效 ,根据最左前缀原则,那么剩余的都会失效  ,所以  typeid应该尽量靠后  ,所以  再次优化后的索引为

	drop index idx_tab on book;   --  删除之前的索引alter table book add index idx_atb (authorid,typeid,bid);   --添加新的索引explain select bid from book where  authorid=1 and  typeid in(2,3) order by typeid desc ; --查看执行计划

可以看到  新的sql  type上升为  ref

1.首先要明白索引的最左前缀原则

2.其次要明白sql的解析顺序

3.in的使用会导致索引列失效  ,所以尽量在where句子中靠后使用,创建索引也尽量靠后建立,这样做也会使执行计划 type升级

4.索引需要逐步优化

本例中同时出现了Using where(需要回原表); Using index(不需要回原表):原因,where  authorid=1 and  typeid in(2,3)中authorid在索引(authorid,typeid,bid)中,因此不需要回原表(直接在索引表中能查到);而typeid虽然也在索引(authorid,typeid,bid)中,但是含in的范围查询已经使该typeid索引失效,因此相当于没有typeid这个索引,所以需要回原表(using where);

例如以下没有了In,则不会出现using where

explain select bid from book where  authorid=1 and  typeid =3 order by typeid desc ;

还可以通过key_len证明In可以使索引失效。

优化案例3-双表优化

create table teacher2
(tid int(4) primary key,cid int(4) not null
);insert into teacher2 values(1,2);
insert into teacher2 values(2,1);
insert into teacher2 values(3,3);create table course2
(cid int(4) ,cname varchar(20)
);insert into course2 values(1,'java');
insert into course2 values(2,'python');
insert into course2 values(3,'kotlin');
commit;

执行以下sql:

select *from teacher2 t left outer join course2 con t.cid=c.cid where c.cname='java';

查看执行计划:

explain select *from teacher2 t left outer join course2 con t.cid=c.cid where c.cname='java';

然后着手进行优化

遵循的原则:

  • 小表驱动大表    就是说  当查询条件  是两个表的两个字段时 ,左边的表数据量小于右边的数据量  性能是最佳的。
  • 如:select  * from  a left  join  b  on  a.id = b.aid  这个时候  a的数据量  小于 b的数据量  性能是最佳的   a的数据量大于  b的数据量时  ,可以改为:

    select  * from  a left  join  b  on   b.aid = a.id

    同理  出现以下语句  

    Select  * from  a ,b  where  a.id = b.aid   and  a.name  =  b.name  

    同样  当 a的数据量  小于  b的数据量时  这个时候性能最佳  

    但是当  a 的数据量  大于 b的数据量时  这个时候就需要改为

    Select  * from  a ,b  where  b.aid = a.id   and  b.name  =  a.name  

    上述  sql中  teacher2  和  course2  数据量  一致  所以  谁在前都一样

索引建立在经常查询的字段上 左插入时一般给左表查询的字段  加索引  ,右插入时  一般给右表所查询的字段加索引

where  常查询的字段  加索引

所以:上述sql语句  不需要更改  然后需要  加索引  分别为:  teacher2  的  cid  以及  course2  的cname

ALTER TABLE teacher2 ADD INDEX index_teacher2_cid(cid);ALTER TABLE course2 ADD INDEX index_course2_cname(cname);

然后可以看到  type 由 ALL 升级到了ref

using join buffer

        还有就是在 没加索引前,extra 出现了一个字段  using join buffer  Mysql引擎使用了 连接缓存,大白话就是sql性能太差了,mysql  不得不  给这个sql加一个缓存  ,所以这个是需要优化的。

优化案例4-三表优化

遵循的原则同上  :

1.  小表驱动  大表  

2.  索引建立在经常查询的字段上  如 左插入的 左表  右插入的右表  以及where 条件的查询字段

优化案例5-其他

1. exist和in

	select ..from table where exist (子查询) ;select ..from table where 字段 in  (子查询) ;

如果主查询的数据集大,则使用In   ,效率高。

如果子查询的数据集大,则使用exist,效率高。

        exist语法: 将主查询的结果,放到子查需结果中进行条件校验(看子查询是否有数据,如果有数据 则校验成功)如果 复合校验,则保留数据;

select tname from teacher where exists (select * from teacher) ;

--等价于select tname from teacher

select tname from teacher where exists (select * from teacher where tid =9999) ;

in:

select ..from table where tid in  (1,3,5) ;

2. order by 优化

        using filesort 有两种算法:双路排序、单路排序 (根据IO的次数)

        MySQL4.1之前 默认使用 双路排序;双路:扫描2次磁盘(1:从磁盘读取排序字段 ,对排序字段进行排序(在buffer中进行的排序)   2:扫描其他字段 )--IO较消耗性能

        MySQL4.1之后 默认使用 单路排序  : 只读取一次(全部字段),在buffer中进行排序。但种单路排序 会有一定的隐患 (不一定真的是“单路|1次IO”,有可能多次IO)。原因:如果数据量特别大,则无法 将所有字段的数据 一次性读取完毕,因此 会进行“分片读取、多次读取”。

        注意:单路排序 比双路排序 会占用更多的buffer。

        单路排序在使用时,如果数据大,可以考虑调大buffer的容量大小:  set max_length_for_sort_data = 1024  单位byte

                如果max_length_for_sort_data值太低,则mysql会自动从 单路->双路   (太低:需要排序的列的总大小超过了max_length_for_sort_data定义的字节数)

提高order by查询的策略:

a.选择使用单路、双路 ;调整buffer的容量大小;

b.避免select * ...  

c.复合索引 不要跨列使用 ,避免using filesort

d.保证全部的排序字段 排序的一致性(都是升序 或 降序)

3. 其他

sql逻辑写的不对也会导致慢

字符集不一致也会导致慢

 优化案例6-关于left join/right join/inner join 索引生效范围说明

Left join: 左表为驱动表,右表被驱动表

Right join: 右边为驱动表,左表为被驱动表

Inner join: mysql一般会选择数据量比较小的表作为驱动表,大表作为被驱动表

优化案例7-order/group by 在join系列

A.没有内联查询的情况:连接查询,中间没有内联查询,order/group by 有索引且位于驱动表上,那么是生效的,反之则不生效,例子如下:

1. Group by 位于驱动表

2. Group by 位于非驱动表

3. Order by 位于驱动表

4. Order by 位于非驱动表

5. 对于位于非驱动表上的order/group  by的优化

(1) inner join,STRAIGHT_JOIN功能和inner join和STRAIGHT_JOIN相同,可以强制使左表成为驱动表,(注意由于mysql优化器的存在,所以有时可能会大表驱动小表,通过执行计划explain查看)例子如下:

Inner join 小表驱动大表

STRAIGHT_JOIN 强制使左表的表作为驱动表

(2) 对于非驱动表上的group by,因为group by默认是先排序后分组,我们对顺序要求不高的情况下,仅仅需要分组,可以使用order by null,来取消排序操作,加快查询,例子如下:

使用order by null 可以减少using filesort

(3) 对于非驱动表上的order by,在无法调整为驱动表的情况下,可以通过适当增加排序区的大小来提高查询效率。

查询排序区Innodb查看Buffer Pool 的个数和大小_如何查看生产环境的buffer pool的大小、buffer pool的数量-CSDN博客 

SHOW VARIABLES LIKE '%innodb_buffer%'

SHOW VARIABLES LIKE '%max_length_for_sort_data%'

调整排序区大小(注意不是越大越好,太大了会很浪费CPU资源)

set max_length_for_sort_data = 1024  单位byte

B. 对于有内联表的情况:order/group by之间有内联查询,那么即使是驱动表的索引,也会失效

以left join为例

有内联查询的情况

Join前

Join 后

Order by

Group by

(一) 情况①

相关问题引出 → 找出年龄组中 最大的手机号的相关信息 ,并按照ID降序排列

EXPLAIN
SELECT *  FROM baseinfo a LEFT JOIN (
SELECT Age,MAX(Phone) phone FROM baseinfo GROUP BY Age )b  ON  a.Age  = b.Age AND a.Phone = b.phone  ORDER BY a.QQID DESC;

执行计划如下

可以看到,这条语句是很糟糕的,没有用到索引,且还有临时表和额外的排序

所以我们的优化策略如下

根据SQL的解析顺序:

from .. on.. join ..where ..group by ....having ...select dinstinct ..order by limit ...

添加索引如下: 

所以,经过分析只要建立复合索引(Age和Phone)以及(QQID)就可以使Group By和Order By都使用上索引,且联合查询也用到索引

按照上述分析,建立索引如下

然后重新查看执行计划为

可以看到,只有内联查询baseinfo 时采用到了复合索引中的Age这个索引(key_len为5),a表用到了复合索引(Age和Phone),索引长度为158,但是QQID主键索引失效,导致了临时表和多余的排序。

相关解释

  • Group By 或者order By 中间最好不要有内联查询,否则即使在他们上面建立了索引,也很有可能失效,例子如上
  • 包装后的表,索引是失效的。例子如下:

首先根据第一条,更改查询语句为:

SELECT * FROM (SELECT *  FROM baseinfo ORDER BY QQID DESC) a 
INNER JOIN (
SELECT Age,MAX(Phone) phone FROM baseinfo GROUP BY Age 
)b  ON  a.Age  = b.Age AND a.Phone = b.phone ;

执行计划如下:

        可以看到,只有包装中的group by  和  order by使用到了索引,但是复合索引(Age_Phone)并没有使用到,因为违反了第二条,On后面的关联条件为包装a,b两个的关联查询,并不能使索引生效,所以我们要将一个表裸露出来,改进如下:

EXPLAIN
SELECT * FROM (SELECT *  FROM baseinfo ORDER BY QQID DESC) a 
INNER JOIN baseinfo c ON a.QQID = c.QQID
INNER JOIN (
SELECT Age,MAX(Phone) phone FROM baseinfo GROUP BY Age 
)b  ON  c.Age  = b.Age AND c.Phone = b.phone ;

执行计划如下


避免索引失效方法:

(1)复合索引

a.复合索引,不要跨列或无序使用(最佳左前缀)(a,b,c)  

b.复合索引,尽量使用全索引匹配(a,b,c)

(2)不要在索引上进行任何操作(计算、函数、类型转换),否则索引失效

select ..where A.x = .. ;  --假设A.x是索引

不要:select ..where A.x*3 = .. ;

复合索引  (authorid,typeid)

explain select * from book where authorid = 1 and typeid = 2 ;--用到了at2个索引

explain select * from book where authorid = 1 and typeid*2 = 2 ;--用到了a1个索引

explain select * from book where authorid*2 = 1 and typeid*2 = 2 ;----用到了0个索引

explain select * from book where authorid*2 = 1 and typeid = 2 ;----用到了0个索引,原因:对于复合索引,如果左边失效,右侧全部失效。(a,b,c),例如如果 b失效,则b c同时失效。

  drop index idx_atb on book ;

alter table book add index idx_authroid (authorid) ;

alter table book add index idx_typeid (typeid) ;

explain select * from book where authorid*2 = 1 and typeid = 2 ; --索引 typeid  生效,单独的索引  前面的失效  不会影响后续的索引

(3)复合索引不能使用不等于(!=  <>)或is null (is not null),否则自身以及右侧所有全部失效。

复合索引中如果有>,则自身和右侧索引全部失效。

explain select * from book where authorid = 1 and typeid =2 ;

-- SQL优化,是一种概率层面的优化,原因是底层有sql优化器。至于是否实际使用了我们的优化,需要通过explain进行推测。

explain select * from book where authorid != 1 and typeid =2 ;
explain select * from book where authorid != 1 and typeid !=2 ;

        体验概率情况(< > =):原因是服务层中有SQL优化器,可能会影响我们的优化。

drop index idx_typeid on book;

drop index idx_authroid on book;

alter table book add index idx_book_at (authorid,typeid);

explain select * from book where authorid = 1 and typeid =2 ;--复合索引at全部使用

explain select * from book where authorid > 1 and typeid =2 ; --复合索引中如果有>,则自身和右侧索引全部失效。

explain select * from book where authorid = 1 and typeid >2 ;--复合索引at全部使用

        ----明显的概率问题---

explain select * from book where authorid < 1 and typeid =2 ;--复合索引at只用到了1个索引

explain select * from book where authorid < 4 and typeid =2 ;--复合索引全部失效

        --我们学习索引优化 ,是一个大部分情况适用的结论,但由于SQL优化器等原因  该结论不是100%正确。

        --一般而言, 范围查询(> <  in),之后的索引失效。

        (4)补救,补救概率问题。尽量使用索引覆盖(using index)(a,b,c)

select a,b,c from xx..where a=  .. and b =.. ;

        (5) like尽量以“常量”开头,不要以'%'开头,否则索引失效

	select * from xx where name like '%x%' ; --name索引失效explain select * from teacher  where tname like '%x%'; --tname索引失效explain select * from teacher  where tname like 'x%';

        explain select tname from teacher  where tname like '%x%'; --如果必须使用like '%x%'进行模糊查询,可以使用索引覆盖 挽救一部分。

        (6)尽量不要使用类型转换(显示、隐式),否则索引失效

explain select * from teacher where tname = 'abc' ;

explain select * from teacher where tname = 123 ;//程序底层将 123 -> '123',即进行了类型转换,因此索引失效

        (7)尽量不要使用or,否则索引失效

explain select * from teacher where tname ='' or tcid >1 ; --将or左侧的tname 失效。

http://www.lryc.cn/news/571214.html

相关文章:

  • 大模型知识库RAG框架,比如LangChain、ChatChat、FastGPT等等,哪个效果比较好
  • 执行 PGPT_PROFILES=ollama make run下面报错,
  • HTML知识全解析:从入门到精通的前端指南(上)
  • (OSGB转3DTiles强大工具)ModelSer--强大的实景三维数据分布式管理平台
  • 内测分发平台应用的异地容灾和负载均衡处理和实现思路?
  • 【前端基础】摩天之建的艺术:html(上)
  • 点云提取车道线 识别车道线
  • Rust 学习笔记:关于 OOP 和 trait 对象的练习题
  • 基于CNN的FashionMNIST数据集识别6——DenseNet模型
  • KingbaseES在线体验平台深度测评:基于MCP接口管理的Oracle风格SQL实战
  • 不同建模方式的介绍 RTL建模笔记(1)
  • 【技术】记一次 Docker 中的 ES 数据迁移,使用 Reindex API
  • C++初阶-queue和deque(队列和双端队列)
  • 台积电(TSMC)工艺库命名规则
  • 在 HTTP 请求返回的状态码不为 200 时,重新发送请求
  • idea中配置svn及提交提示“未检测到更改”提示
  • 条件查询详细说明
  • 为编写Python脚本实现H3C交换机自动化配置笔记一
  • Java编程之外观模式
  • JSON编辑器:三种模式的应用(三)
  • 重构气血经络的数学模型:气血经络级联控制系统核心方程
  • 第17天:数据库学习笔记1
  • MySQL 默认连接数
  • .sstop勒索加密数据库恢复---惜分飞
  • 从电商平台下载数据的项目经验分享 (part 1)
  • 前后端拦截器+MDC实现纯数字 traceId 全链路日志追踪(axios + Spring Boot 超详细实战)
  • DeepSeek 大型 MoE 模型大规模部署压测学习
  • FlinkCDC-Hudi数据实时入湖原理篇
  • JVM监控的挑战:Applications Manager如何提供帮助
  • Spring Boot集成Kafka全攻略:从基础配置到高级实践