技术干货实战(10) - 值得一看的常见SQL语句性能优化方式

作者: 修罗debug
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。



作为一名开发者,想必每一天都会跟数据库打交道,像MySQLOracleSqlServer等都是常见的数据库产品;而打交道的方式无非就是建库、建表、写SQL、写存储过程、写视图、写触发器等等,其中,“写SQL”应当是最常做的事情了,本文将罗列整理出SQL语句常见的几十种性能优化方式,建议诸位可以收藏!

   

1.SQL查询应尽量避免全表扫描,首先应考虑的是在 where order by 涉及的列上建立索引;

2.应尽量避免在 where 子句中对字段进行 null 值判断,创建表时NULL是默认值,但大多数时候应该使用NOT NULL,或者使用一个特殊的值,如0-1作为默认值;

3.应尽量避免在 where 子句中使用 != <> 操作符,MySQL只有对以下操作符才使用索引:<<==>>=BETWEENIN,以及某些时候的LIKE

4.应尽量避免在 where 子句中使用 or 来连接条件,否则将有可能导致引擎放弃使用索引而进行全表扫描,可以考虑使用 UNION合并查询,比如:   

select name from t where id=10 union all select name from t where id=20;


5. 慎用in not in,否则很可能会导致全表扫描,对于连续的数值,能用 between 就不要用 in 了,比如:

select name from t where age between 1 and 3;


6.下面的查询也将导致全表扫描:select id from t where name like %abc%’ 或者select id from t where name like %abc’若要提高效率,可以考虑全文检索;select id from t where name like abc%’才用到索引;

7.如果在 where 子句中使用参数,也会导致全表扫描;

8.应尽量避免在 where 子句中对字段进行表达式操作,以及尽量避免在where子句中对字段进行函数操作;


9.很多时候用 exists 代替 in 是一个好的选择,比如 select id,age from a where id  in(select id  from b).用下面的语句替换:   

select id,age from a where exists(select 1 from b where id=a.id);


10.索引固然可以提高select 的效率,但同时也降低了 insert update 的效率,因为 insert update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定;一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要;

 

11.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销;

 

12.尽可能的使用 varchar/nvarchar 代替 char/nchar , 因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些;

 

14.最好不要使用 * 返回所有: select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段:select id,age,name from t

 

15.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理;

 

16.使用表的别名(Alias):当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个字段列上,这样一来,就可以减少解析的时间并减少那些由字段列歧义引起的语法错误;

 

17.使用“临时表”暂存中间结果:

简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能;

 

18.一些SQL查询语句应加上nolock:

读、写是会相互阻塞的,为了提高并发性能,对于一些查询,可以加上nolock,这样读的时候可以允许写,但缺点是可能读到未提交的脏数据;使用 nolock3条原则:查询的结果用于“插、删、改”的不能加nolock ;查询的表属于频繁发生页分裂的,慎用nolock ;使用临时表一样可以保存“数据前影”,起到类似Oracleundo表空间的功能,能采用临时表提高并发性能的,不要用nolock

 

19.常见的简化规则如下:不要有超过5个以上的表连接(JOIN),考虑使用临时表或表变量存放中间结果;少用子查询,视图嵌套不要过深,一般视图嵌套不要超过2个为宜;

 

20.将需要查询的结果预先计算好放在表中,查询的时候再Select;这在SQL7.0以前是最重要的手段:例如医院的住院费计算。

 

21.IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数;

 

22.尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程;存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句,是控制流语言的集合,速度当然快;反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在Tempdb

 

23,尽量使用“>=”,不要使用“>”;

 

24.索引的使用规范:索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引;尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引;避免对大表查询时进行table scan,必要时考虑新建索引;在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用;要注意索引的维护,周期性重建索引,重新编译存储过程。  

 

25.下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:   

SELECT * FROM record WHERE substrINg(card_no,1,4)=’5378’ (13秒) 

SELECT * FROM record WHERE amount/30< 1000 (11秒)

SELECT * FROM record WHERE convert(char(10),date,112)=’19991201’ (10秒)

分析:

WHERE子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样: 

SELECT * FROM record WHERE card_no like ‘5378%’ (< 1秒) 

SELECT * FROM record WHERE amount< 1000*30 (< 1秒)

SELECT * FROM record WHERE date= ‘1999/12/01’ (< 1秒)


26.当有一批数据需要批量插入或更新时,应该更多的是用批量插入或批量更新的方式,而不建议一条一条记录的去更新;

 

27.提高GROUP BY语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果,但第二个明显就快了许多.

低效版:   

SELECT JOB , AVG(SAL) FROM EMP GROUP BY JOB HAVING JOB =’PRESIDENT’ OR JOB =’MANAGER’

高效版: 

SELECT JOB , AVG(SAL) FROM EMP WHERE JOB =’PRESIDENT’OR JOB =’MANAGER’ GROUP BY JOB


28Oracle数据库的SQL语句用大写,因为Oracle总是先解析SQL语句,把小写的字母转换成大写的再执行;

29.别名的使用,别名是大型数据库的应用技巧,就是表名、列名在查询中以一个字母为别名,查询速度要比建连接表快1.5倍。

30.避免死锁,在你的存储过程和触发器中访问同一个表时总是以相同的顺序;事务应经可能地缩短,在一个事务中应尽可能减少涉及到的数据量;永远不要在事务中等待用户输入;

31.最好不要使用触发器,触发一个触发器,执行一个触发器事件本身就是一个耗费资源的过程;如果能够使用约束实现的,尽量不要使用触发器;不要为不同的触发事件(InsertUpdateDelete)使用相同的触发器;不要在触发器中使用事务型代码;

32索引创建规则:

1)表的主键、外键必须有索引;

2)数据量超过300的表应该有索引;

3)经常与其他表进行连接的表,在连接字段上应该建立索引;

4)经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;

5)索引应该建在选择性能高效的字段上;

6)索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;

7)复合索引的建立需要进行仔细分析,尽量考虑用单字段索引代替;

8)正确选择复合索引中的主列字段,一般是选择性较好的字段;

9)复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;

10)如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;

11)如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;

12)如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;

13)频繁进行数据操作的表,不要建立太多的索引;

14)删除无用的索引,避免对执行计划造成负面影响;

15)表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大。

16)尽量不要对数据库中某个含有大量重复的值的字段建立索引。


33.MySQL查询优化总结:

1)使用慢查询日志去发现慢查询,使用执行计划去判断查询是否正常运行,总是去测试你的查询看看是否他们运行在最佳状态下;久而久之性能总会变化,避免在整个表上使用count(*),它可能锁住整张表,使查询保持一致以便后续相似的查询可以使用查询缓存;

2)在适当的情形下使用GROUP BY而不是DISTINCT,在WHERE, GROUP BYORDER BY子句中使用有索引的列,保持索引简单,不在多个索引中包含同一个列,有时候MySQL会使用错误的索引,对于这种情况使用USE INDEX,检查使用SQL_MODE=STRICT的问题,对于记录数小于5的索引字段,在UNION的时候使用LIMIT而不是使用OR

3)为了 避免在更新前SELECT,使用INSERT ON DUPLICATE KEY或者INSERT IGNORE ,不要用UPDATE去实现,不要使用 MAX,使用索引字段和ORDER BY子句,LIMIT MN实际上可以减缓查询在某些情况下,有节制地使用,在WHERE子句中使用UNION代替子查询,在重新启动的MySQL,记得来温暖你的数据库,以确保您的数据在内存和查询速度快,考虑持久连接,而不是多个连接,以减少开销,基准查询,包括使用服务器上的负载,有时一个简单的查询可以影响其他查询,当负载增加您的服务器上,使用SHOW PROCESSLIST查看慢的和有问题的查询,在开发环境中产生的镜像数据中 测试的所有可疑的查询。


34.我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。


35.MySQL查询可以启用高速查询缓存,这是提高数据库性能的有效MySQL优化方法之一。当同一个查询被执行多次时,从缓存中提取数据和直接从数据库中返回数据快很多。


36.EXPLAIN SELECT 查询用来跟踪查看效果:

使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的,这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。


37.当只要一行数据时使用 LIMIT 1

当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。


38.选择表合适存储引擎:

1Myisam: 应用时以读和插入操作为主,只有少量的更新和删除,并且对事务的完整性,并发性要求不是很高的。

2Innodb: 事务处理,以及并发条件下要求数据的一致性。除了插入和查询外,包括很多的更新和删除。(Innodb有效地降低删除和更新导致的锁定)。对于支持事务的InnoDB类型的表来说,影响速度的主要原因是AUTOCOMMIT默认设置是打开的,而且程序没有显式调用BEGIN 开始事务,导致每插入一条都自动提交,严重影响了速度。可以在执行sql前调用begin,多条sql形成一个事物(即使autocommit打开也可以),将大大提高性能。


39.优化表的数据类型,选择合适的数据类型:

原则:更小通常更好,简单就好,所有字段都得有默认值,尽量避免null

例如:数据库表设计时候更小的占磁盘空间尽可能使用更小的整数类型.(mediumint就比int更合适)


比如时间字段:datetimetimestamp, datetime占用8个字节,而timestamp占用4个字节,只用了一半,而timestamp表示的范围是19702037适合做更新时间

MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。

因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小;


40.任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边


说在最后的话:

最近正值跳槽面试季,debug特意录制来一套课程:Java工程师核心技术-典型案例与面试实战系列二 ,课程主要内容在:1.以企业实际业务场景、功能需求为驱动,采用各种主流的技术进行实现;2.理论为辅、实战为主,以实现业务功能需求为最终目标,追求性能、从一而终;3.深入底层,结合应用场景剖析技术的实现原理;

课程大纲如下图所示:



   

详细课程目录以及课时内容可以在PC端打开链接观看:   https://www.fightjava.com/web/index/course/detail/16


目前正值优惠阶段,原价99元,现在只需要39.9 即可学习整个课程,感兴趣的小伙伴可以加debug的微信:debug0868  咨询购买与学习!

我是debug,一个相信技术改变生活、技术成就梦想 的攻城狮;如果本文对你有帮助,请关注公众号,并动动手指收藏、点赞、以及转发哦!!!