数据库表结构设计,sql性能分析与优化
我们就擦肩而过了,关注我吧!
原创
实战干货
往期数据库表结构优化设计系列文章:
1、
2、
3、
相信每个开发同事,都应该被DBA或运维同事吐槽过慢sql问题,有的可能DBA直接帮你优化了,但是也有很多公司没有DBA,这个时候就需要我们开发人员自己来排查为什么我的这个sql会这么慢?是否还有优化空间?
一、如何分析寻找慢sql原因
mysql给我们提供了解决办法,查看执行计划,使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。帮助我们寻找性能瓶颈。
以mysql8.0.20版本,执行计划各字段解释如下:
id:代表表的读取顺序,id越大,越先执行,若id相同,执行顺序由上到下;
select_type:数据读取操作的操作类型,主要用于区别普通查询、联合查询、子查询等的复杂查询,select_type类型如下: 1、SIMPLE:简单的查询,查询中不包含子查询或者union; 2、PRIMARY:查询中包含复杂查询,最外层标记为PRIMARY; 3、SUBQUERY:在SELECT或者WHERE列表中包含了子查询; 4、DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表里; 5、UNION:若第二个SELECT出现在UNION之后,则被标记为UNION,若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED; 6、UNION RESULT:从UNION表获取结果的SELECT; |
table:显示这一行的数据是来源哪张表的;
partitions:查询使用到表分区的分区名;
tye:显示查询使用了何种类型,注意:我们日常业务系统sql要尽可能达到range级别,最好是ref级别; 从最好到最差依次是:system>const>eq_ref>ref>range>index>ALL 1、system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计; 2、const:表示通过索引一次就找到了,const用于主键索引或者唯一索引。因为只匹配一行数据,所以很快。如将主键至于where列表中,MySQL就能将该查询转换为一个常量; 3、eq_ref:唯一性索引,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描; 4、ref:非唯一索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体; 5、range:只检索给定范围的行,使用了索引,然后在你的where语句中出现了between、<、>、in等的查询,这种范围扫描索引扫描比全表扫描要好,因为他只需扫描部分数据,不用扫描全部索引; 6、index:遍历索引树以找到匹配行; 7、all:遍历全表,以找到匹配行,index通常比ALL快,因为索引文件通常比数据文件小; |
possible_keys:查询涉及的字段上若存在索引,则该索引将被列出,但索引不一定被查询实际使用
key:实际使用的索引,若没有使用索引,则为null;
key_len:表示索引中使用的字节数,key_len显示的值为索引最大可能长度,并非实际使用长度;
ref:哪些列或常量被用于查找索引列上的值,可能是一个常数;
rows:大致估算找到所需的记录,所需要读取的行数;
Extra:十分重要的额外信息,最好情况就是Using index,Using where; 1、Using filesort:mysql无法使用索引完成排序操作,而变成文件排序; 2、Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by; 3、Using index:表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找; 4、Using where:表示使用了where过滤; 5、Using join buffer:使用了连接缓存; 6、impossible where:where子句查询的值总是空,不能用来获取任何数据; 7、distinct:优化distinct,在找到第一匹配的元组后即停止找同样值的工作; |
总结:通过对执行计划结果分析,我们就可以找到慢sql的原因,其中大部分的原因就是索引失效或者是没加索引导致的;
二、索引失效
假设我们建了一个复合索引,索引使用情况,如下图所示
索引失效情况总结:
1、最左前缀法则:如果是多列的复合索引,我们的sql where条件里的列,从左向右要和索引列顺序保持一致,且不能跳过索引中间的列,但是可以跳过后面的列;
2、在索引列上进行操作包括(函数、计算、自动或手动类型转换),会导致索引失效而转向扫描全表;
3、尽量使用覆盖索引(索引列和查询列一致),减少select *;
4、使用不等于(!=或者<>)的时候无法使用索引,会导致全表扫描;
5、is null,is not null 也无法使用索引;
6、like以 % 通配符开头,mysql索引失效会变成全表扫描操作;
7、or 会使索引失效;
8、使用了大于号>或小于<号的列,后面列索引会失效;
速记口诀如下图:
三、其他优化查询,in和exists
注:in先执行子查询,后执行外层查询;exists先执行外层查询,后执行子查询