当你访问网站的时候,有的时候会慢的想让你砸电脑,这个时候服务器要背锅了吗? 不,要背锅的不仅仅是服务器,数据库也有很大责任,不负责任的sql开发者更会让你崩溃的.为了提高sql响应速度,还是好好了解下sql的优化吧

sql优化的方式

一:sql性能分析

sql优化首先要对sql的消耗时间,或者使用频率进行分析,然后在进行其他操作.具体性能分析请看我的另一篇博客

 MySQL进阶:sql性能分析_王会举的博客-CSDN博客

二:   explain关键字  

 使用explain关键字来查看当前sql语句的执行情况,来对症下药.因为内容较多,放在最后进行讲解

三:   正确的建立索引

索引的建立对sql的影响是非常大的,如果对索引不太理解的可以看我另一篇文章: ​​​MySQL进阶:索引的使用及理解_王会举的博客-CSDN博客_mysql索引的作用与使用  

select  * from user_test  WHERE uid=15988;
select  * from user_test  WHERE address=15988;  可以看到有索引的查找速度非常快.

四:剩下的就是零碎的对sql语句进行优化

3:SQL语句中IN包含的值不应过多:  

例如:select id from t where num in(1,2,3) 对于连续的数值,能用between就不要用in了; 实测速度差距不是很大.

4:SELECT语句务必指明字段名称:  

禁止用 * 来查询 ,禁止用 * 来查询 ,禁止用 * 来查询 , 查找哪个字段,就写具体的字段.

select  * from user_test  WHERE address=15988;
select  address from user_test  WHERE address=15988;

5:只查询一条数据的时候,使用limit 1

6:避免在where子句中对字段进行null值判断: 

对于null的判断会导致引擎放弃使用索引而进行全表扫描。(此处存在疑点,我本人测试的时候,发现索引还是能使用到)

更新:  mysql会自动判断数据的分布情况  判断数据中 null 多 还是 not null 多,   然后决定走不走索引.

explain select  uid from user_test  WHERE phone is null; 

7:避免在where子句中对字段进行表达式操作: 


select  uid from user_test  WHERE uid*10=40;
上面的sql对字段就行了算术运算,这会造成引擎放弃使用索引,建议改成:
select  uid from user_test  WHERE uid=40/10;

 

8:对于联合索引来说,要遵守最左前缀法则: 

例如组合索引(id,name,sex)  使用的时候,可以id 或者id,name .  禁止直接name,或者sex.会导致联合索引失败

注意: id, name,sex 这三个字段填写顺序不会有影响,  mysql会自动优化成最左匹配的顺序.

前三条sql都能命中索引,中间两条由于不符合最左匹配原则,索引失效. 

最后一条sql 由于有最左索引id 所以索引部分成功,部分失效.  id字段索引使用成功.

explain select * from  `user_test` where uid=10  ; 
explain select * from  `user_test` where uid=10 and name='张三'; 
explain select * from  `user_test` where uid=10 and name='张三' and phone='13527748096'; 

explain select * from  `user_test` where name='张三' and phone='13527748096'; 
explain select * from  `user_test` where name='张三'; 

explain select * from  `user_test` where uid=10 and phone='13527748096'; 

9: 尽量使用inner join,避免left join:

如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。

10:注意范围查询语句: 

 对于联合索引来说,如果存在范围查询,比如between、> >= < <=等条件时,可能会造成后面的索引字段失效。索引失效是因为mysql发现全表扫描比走索引效率更高,因此就放弃了走索引
当Mysql发现通过索引扫描的行记录数超过全表的10%-30%时,优化器可能会放弃走索引,自动变成全表扫描。

explain select * from  `user_test` where uid=10 and name='张三' and phone='13527748096'; 
explain select * from  `user_test` where uid between( 1 and 10) and name ='张三' and phone='13527748096'; 
 

11:不建议使用%前缀模糊查询:

例如 : LIKE“%name”或者LIKE“%name%”,这种查询会导致索引失效而进行全表扫描。但是可以使用LIKE “name%”。

explain select * from  `user_test` where uid=10 and  uid like  "%1" ; 
explain select * from  `user_test` where uid=10 and  uid like  "1%" ; 

12:在 where 子句中使用 or 来连接条件,如果or连接的条件有一方没有索引,将导致引擎放弃使用索引而进行全表扫描

解决办法: 将or连接的双方都建立索引,就可以使用. 

explain select * from  `user_test` where  uid=10 or name='张三'; 

13:应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。(此处存在疑点,我本人测试的时候,发现索引还是能使用到)

explain select uid from  `user_test` where uid=1 and  substring(phone,1,3)='135'

explain select uid from  `user_test` where uid=1 and   abs(uid)=1;

14: 字符串类型的字段 查询的时候如果不加引号''  ,会导致自动进行隐式转换,然后索引失效

15:指定查询的索引

当sql查询的字段有多个索引的时候,mysql优化器会自动选择一个索引进行查询,我们也可以通过sql字段进行自定义,

15.1: use index(索引): 推荐使用指定的索引  (最终用不用该索引,还需要mysql自己判断)

select * from  use index(索引A)  

15.2: ignore index(索引) : 忽略掉这个索引

select * from  ignore index(索引A)  

15.3: force index(索引): 强制使用该索引

select * from  force index(索引A)  

16: insert 优化

 16.1: 需要插入多条数据的时候 使用批量插入.(多次插入需要频繁的建立连接.浪费资源) 

 16.2: 多次插入数据时,采用手动提交事务

  sql语句在执行的时候如下所示, 会自动开启事务和提交事务.  我们可以手动开启事务,然后执行多条sql后,在手动提交事务.减少资源浪费.

start transaction;   执行sql;   commit;

 17: order by 排序优化 (排序时,使用有索引的字段进行排序)

使用order by排序时,会出现两种情况  (explain查看Extra字段)

1:using fileSort : 全表扫描,读取出数据,然后再排序缓冲区进行排序.  (排序字段没有索引)

2:using index: 通过索引直接返回有序的数据. 不需要额外排序(有索引,效率高)

18: count 优化     速度:count(*)>count(1)>count(字段)

 inndb引擎的使用如下  (MyIASM默认存了数据总数,所以效率最高)

1:count(字段):遍历整张表 会把每一行的字段值取出来,然后返回

2:count(1): 便利整张表,但不取值,对于返回的数据,放入1进去.然后累加

3:count(*):inndb引擎,特意做了优化,不会取出值,直接服务层进行累加

19:update优化  (避免出现表锁)

innodb引擎使用update时,会有行锁/表锁两种模式, 如果where 字段没有索引的时候会升级成表锁,

update table set xx=1 where  name=xx   (name没有索引,此时是表锁)
update table set xx=1 where  id=xx  (id有索引,此时是行锁)

20: 创建表时使用同一的编码

mysql多表联查时,如果表的字符集不一样,会有一个数据类型转换的过程.

例如 utf8 与utf8mb4前者是3字节unicode编码,后者是4字节unicode编码. 此时如果多表查询,则索引会失效

sql优化的语句将会持续更新,目前所知的就这些.

五: explain关键字分析:

    explain是非常重要的关键字,要善于运用它.  通过explain我们可以获得以下信息:

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

   使用方法:explain + sql语句。会出现下方截图.根据下方的字段来进行解析sql的问题所在

例如:  explain SELECT user_name FROM `user` WHERE id ="1060"

1:id字段:

 id可以认为是查询序列号,每一个id代表一个select,一句sql有两个select,就会有两列,两个id.,不同的id代表不同子查询,id越大优先级越高,越先被解析,如果id相同,则按照从上到下的顺序查找.

2:select_type:表示查询的类型

(1) SIMPLE(简单SELECT,不使用UNION或子查询等)

(2) PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)

(3) UNION(UNION中的第二个或后面的SELECT语句)

(4) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)

(5) UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)

(6) SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)

(7) DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)

(8) DERIVED(派生表的SELECT, FROM子句的子查询)

(9) UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)

3:table:输出结果集的表

显示这一行的数据是关于哪张表的,有时不是真实的表名字,可能是简称

4:type:表示表的连接类型

对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行

index: Full Index Scan,index与ALL区别为index类型只遍历索引树

range:只检索给定范围的行,使用一个索引来选择行

ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件

const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system

NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

5:possible_keys:表示查询时,可能使用的索引

指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)

该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询

6:key:表示实际使用的索引

key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中

如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

7:key_len:索引字段的长度

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

不损失精确性的情况下,长度越短越好 

8:ref:列与索引的比较

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值


9:rows:扫描出的行数(估算的行数)

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数



10:Extra:执行情况的描述和说明

该列包含MySQL解决查询的详细信息,有以下几种情况:

Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by

Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”

-- 测试Extra的filesort
explain select * from emp order by name;
Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。

Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

No tables used:Query语句中使用from dual 或不含任何from子句

-- explain select now() from dual;

explain关键字 转载至:MySQL Explain详解 - 杰克思勒(Jacksile) - 博客园 (博主写的非常棒)

参考文章: 史上最全SQL优化方案_ITPUB博客

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐