sql执行计划与优化

Easter79
• 阅读 813

在我们实际工作中大部分人会遇到sql优化的问题,这篇文章主要介绍SQL优化相关。首先我们怎么发现我们的sql执行效率低呢,最简单的方法就是当用户反馈慢的时候我们就会知道哪里可能会有sql效率影响的问题,这里排除其他影响情况,只考虑数据库sql慢的问题。当然这种方式对于我们来说很被动,我们还可以通过什么方式找到有性能问题sql,我们可以通过MySQL的配置文件来开启慢查询日志,我们可以设置slow_query_log=on,因为MySQL默认是不开启,如果MySQL是运行状态的MySQL可以使用set global命令来启动,我们还可以指定日志文件路径slow_query_log_file,如果不设置默认在MySQL数据目录中,除了文件路径,我们还可以设置记录sql执行时间的伐值long_query_time,可以精确的毫秒级别,但是他的单位是秒,如果不设置默认为10s,log_queries_not_using_indexes配置会记录未使用索引的sql,因为我们知道索引的建立会占用一定的磁盘空间,对添加修改删除也会有影响,如果一个索引的建立一直未被使用,我们可以对其进行分析,是重复索引了还是冗余了等问题,关于MySQL的配置文件的详细配置可以自行查阅资料,MySQL配置位置文件的位置在linux服务器我也就不多去介绍了,对于使用docker镜像安装的MySQL可以指定外挂我们自己的配置文件。MySQL的慢查询日志可能会记录很多,我们肉眼去看可能不是特别现实,我们可以借助慢查询日志分析工具,例如MySQL官方推荐的mysqldumpslow,在MySQL服务器自带mysqldumpslow工具,可以直接使用。我们除了可以通过日志记录的方式查看也可以通过语句实时查询有幸能问题的sql。

  sql执行计划与优化

  sql执行计划与优化

  我们可以获取到进程id,用户,ip,使用的数据库,语句,时间等。我们在进行sql分析前先来了解一下怎么查看sql的执行计划,执行计划的每个参数又是什么意思呢?我们继续向下看。

什么是sql的执行计划,他就像一份体检报告,这份报告告诉你没想参数的结果。我们通过EXPLAIN来查看我们的sql执行的情况,贴个图

sql执行计划与优化

  sql执行计划与优化

  这是一个很简单的sql两个表关联查询通过where子查询作为条件,从第一个列来看,有1跟2,再看sql我们能看出来MySQL首先执行的应该是where条件里的子查询,通过id这列我们能看出来MySQL执行时id越大执行优先级越高,如果id相同则顺序执行。

第二列select_type查询类型。查询类型都有:

SIMPLE:简单查询,查询里没有子查询或者union

PRIMARY:查询包含嵌套查询,最外层查询的类型为PRIMARY

SUBQUERY:在SELECT或WHERE列表中包含了子查询,子查询的类型为SUBQUERY

DERIVED(衍生):在FROM列表中的子查询的类型为DERIVED,MySQL会将查询的结果放在临时表中,我们上面写到的sql没有产生这个类型只需要将store写成子查询即可,我们就不做演示,我们的查询有的时候需要临时表他会加快我们的查询效率,但是临时表的数据量很大,它也会影响我们的查询效率,对于临时表我们后面会提到

UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在  FROM子句的子查询中,外层SELECT将被标记为:DERIVED

UNION RESULT:从UNION表获取结果的SELECT的类型为UNION RESULT

UNION跟UNION RESULT光看理论可能看不懂,举个例子

  sql执行计划与优化

  sql执行计划与优化

  这样一看就明白了,出现在union后面的为union类型,而两个表合并的为union result类型

第三列看名字就知道他的意思是表

第四列表的分区(看下图),对于分区这里暂不介绍

第五列访问类型,常见的从低至高all,index,range,ref,eq_ref,const,system。当然system表里只有一行记录,实际业务表基本不会出现,可以忽略。

const:通常通过主键索引或者唯一索引进行查询,因为只需要匹配一行,所以很快。

  sql执行计划与优化

  sql执行计划与优化

eq_ref:通过主键或者唯一索引相等作为条件,对于每个索引只有唯一匹配,举个例子:

sql执行计划与优化

  ref:查询的索引列查询出来的记录不唯一,没有唯一匹配,举个例子:

sql执行计划与优化

  他跟eq_ref区别就在于一个是完全匹配,一个是查询的结果不唯一,所以前者更多用于主键索引或者唯一索引

  range:范围索引,通过'>','<',in,between,like

   sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  注意我们上篇文章提到不是所有的like查询都会走索引

  index:全索引扫描,我们知道索引的大小远小于数据的大小,即使是index他的查询效率也比全表扫描高很多

  sql执行计划与优化

  查询出来的列值在索引中不需要再去找到对应行的数据,也叫覆盖索引

  all:全表扫描,这个很简单就不写例子了

  第六列possible_keys可能用到的索引,当查询的列,多个索引列包含,会列出可能执行的索引

  第七列key真正执行的索引如果该列为空可能是没有索引或者索引失效

  第八列key_len索引的最大可能长度,越大代表查询用到的索引更多,但并非越大越好,在同样查询结果的情况下key_len越小越好 

   sql执行计划与优化

  sql执行计划与优化

   第九列ref查询中使用索引被用到的值或常量

   第十列查询需要扫描的行,这恶值越小越好

第十一列代表查询的结果占扫描列的百分比,上图都是100,这里不做介绍

第十二列额外重要的信息,先把比较重要的放到前面说

Using index代表我们查询用到了索引

Using temporary代表我们的查询通过临时表存储,相当于创建一张表然后将数据复制进去比较出结果后邮件临时表删除,这样的索引使也会严重影响查询效率,最常见用于分组或者排序,举个例子,创建的索引如下

sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  还能举出例子的情况还有很多种,这里就不一一列举了,通过几个执行计划能看出来在我们查询时通过复合索引进行了分组的时候,当访问类型为ref级别的时候只有在复合索引跳过某个字段的时候会出现,在访问类型为range级别时,group by后面的字段必须按照符合索引的顺序,后面的可以缺失,第一个不能缺失,store_name,store_user_name,status|store_name,store_user_name|store_name不会出现Using temporary,store_user_name,status等都会出现Using temporary,因为range为范围查找,范围查找后的所有条件都失效,所以没有使用后面的索引。导致出现Using temporary

Using filesort代表MySQL无法通过索引进行排序,通过文件排序的的方式进行排序

sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

  sql执行计划与优化

   几个例子发现它的出发条件跟上面的触发条件一样,同样排序没有通过索引的方式排序通过文件排序也是会影响查询效率的

  Using where表示使用了where条件

  Using join buffer使用了连接缓存

  impossible where表示where的条件总是查询不出结果,我们的状态启用或者禁用,不可能一个店又是启用又是禁用

  sql执行计划与优化

  说了这么多,举几个例子来看一下具体优化:

  1.单表查询:

  select * from store where store_name='测试' and store_user_name LIKE'测试%' GROUP BY `status`,我们还是用这条sql来看,前面大致讲了一下原因,但是没有说这条sql应该怎么改,我们还是贴出执行计划

  sql执行计划与优化

  创建的索引列也贴出来,这样就不用去上面找了

  sql执行计划与优化

  这条sql能看出来使用了索引,访问类型是range,也就是说范围查询后面的列是不走索引的,按我们有什么办法让他走索引呢,我们上面把like查询换成了等值查询之后就走了索引,但是这样做并不满足我们的优化需求,为了在满足业务正确性的前提下我们可以优化索引

  我们将复合索引中需要范围查询的索引去掉

  sql执行计划与优化

  然后看一下执行计划

  sql执行计划与优化

  发现访问类型为ref,从这里就能看出来我们的优化有了效果,并且我们也解决了Using temproary跟Using filesort,key_len的长度也减少了,前面也提到在同样的查询结果下key_len越小越好

  2.两表联查:

  select * from store s LEFT JOIN user_account ua on s.store_mobile=ua.mobile,我们将两个表的索引都删掉,执行

  sql执行计划与优化

   毫无疑问两个都是全表扫面,那么问题来了,我们加索引应该怎么加,给哪个表加,我们先试一下从第一个表开始

  sql执行计划与优化

  sql执行计划与优化

  发现加了索引之后访问类型没有改变但是我们扫描的列变少了,这可能是MySQL查询优化器觉得全表扫描更优,然后我们将store表索引删除,在user_account表上加索引

  sql执行计划与优化

  我们能看出来我们加索引的表应该是右边的表,right on一样我们就不做演示了,我们反着加,三个表联查我们也是在后面两个表加,我们在加索引的过程一定要注意索引在什么情况下会失效,上篇文章我们提到过,解决索引失效也是我们优化sql的一部分,在满足业务的情况下当然我们也可以用小表驱动大表,还有一个特殊的请款列出来

  sql执行计划与优化

  order by遵循最左匹配原则,但是这种情况例外,将会产生Using filesort。

  本篇文章提到的优化方案不一定是最优的,可能也有其他多种方案,文章可能也会有漏洞,欢迎大佬吐槽。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k