SQL优化方法
Posted xj-record
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL优化方法相关的知识,希望对你有一定的参考价值。
以下是网上流传比较广泛的30种SQL查询语句优化方法:
应尽量避免在 where
子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where
及 order by 涉及的列上建立索引。
应尽量避免在 where
子句中对字段进行 null
值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,
如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10 union all select id from t where num=20
下面的查询也将导致全表扫描:(不能前置百分号)
select id from t where name like ‘%c%’
若要提高效率,可以考虑全文检索。
in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
如果在 where
子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。
如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
如:
select id from t where substring(name,1,3)=’abc'
生成的id应改为:
select id from t where name like ‘abc%’
如:
select id from t where datediff(day,createdate,’2005-11-30′)=’2005-11-30′
应改为:
select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。
不要写一些没有意义的查询,如需要生成一个空表结构:select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:create table #t(…)
很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
索引并不是越多越好,索引固然可以提高相应的 select
的效率,但同时也降低了 insert
及 update
的效率,因为 insert
或 update
时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
应尽可能的避免更新 clustered
索引数据列,因为 clustered
索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
尽可能的使用 varchar/nvarchar 代替 char/nchar (如果一个字段的值是固定长度的,则尽量使用char类型),因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
避免频繁创建和删除临时表,以减少系统表资源的消耗。
临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。
在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into
代替 create table
,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table
,然后 drop table
,这样可以避免系统表的较长时间锁定。
尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD
游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON
,在结束时设置 SET NOCOUNT OFF
。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC
消息。
尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
尽量避免大事务操作,提高系统并发能力。
----
分享:MySQL数据库设计总结
作者针对mysql数据库设计总结了19条规则,其中对于大表优化,他在规则7中提到: 大表可以考虑水平拆分。
大表影响查询效率,根据业务特性有很多拆分方式,像根据时间递增的数据,可以根据时间来分;以id划分的数据,可以根据id%数据库个数的方式来拆分。
19条规则摘要如下:
规则1:一般情况可以选择MyISAM存储引擎,如果需要事务支持必须使用InnoDB存储引擎。
注意:MyISAM存储引擎 B-tree索引有一个很大的限制:参与一个索引的所有字段的长度之和不能超过1000字节。另外MyISAM数据和索引是分开,而InnoDB的数据存储是按聚簇(cluster)索引有序排列的,主键是默认的聚簇(cluster)索引,因此MyISAM虽然在一般情况下,查询性能比InnoDB高,但InnoDB的以主键为条件的查询性能是非常高的。
规则2:命名规则。
1、数据库和表名应尽可能和所服务的业务模块名一致
2、服务与同一个子模块的一类表应尽量以子模块名(或部分单词)为前缀或后缀
3、表名应尽量包含与所存放数据对应的单词
4、字段名称也应尽量保持和实际数据相对应
5、联合索引名称应尽量包含所有索引键字段名或缩写,且各字段名在索引名中的顺序应与索引键在索引中的索引顺序一致,并尽量包含一个类似idx的前缀或后缀,以表明期对象类型是索引。
6、约束等其他对象也应该尽可能包含所属表或其他对象的名称,以表明各自的关系
规则3:数据库字段类型定义
1、经常需要计算和排序等消耗CPU的字段,应该尽量选择更为迅速的字段,如用
TIMESTAMP
(4个字节,最小值1970-01-01 00:00:00)代替Datetime
(8个字节,最小值1001-01-01 00:00:00),通过整型替代浮点型和字符型
2、变长字段使用varchar,不要使用char
3、对于二进制多媒体数据,流水队列数据(如日志),超大文本数据不要放在数据库字段中
规则4:业务逻辑执行过程必须读到的表中必须要有初始的值。避免业务读出为负或无穷大的值导致程序失败
规则5:并不需要一定遵守范式理论,适度的冗余,让Query尽量减少Join
规则6:访问频率较低的大字段拆分出数据表。有些大字段占用空间多,访问频率较其他字段明显要少很多,这种情况进行拆分,频繁的查询中就不需要读取大字段,造成IO资源的浪费。
规则7:大表可以考虑水平拆分。大表影响查询效率,根据业务特性有很多拆分方式,像根据时间递增的数据,可以根据时间来分。以id划分的数据,可根据id%数据库个数的方式来拆分。
一.数据库索引
规则8:业务需要的相关索引是根据实际的设计所构造sql语句的where条件来确定的,业务不需要的不要建索引,不允许在联合索引(或主键)中存在多于的字段。特别是该字段根本不会在条件语句中出现。
规则9:唯一确定一条记录的一个字段或多个字段要建立主键或者唯一索引,不能唯一确定一条记录,为了提高查询效率建普通索引
规则10:业务使用的表,有些记录数很少,甚至只有一条记录,为了约束的需要,也要建立索引或者设置主键。
规则11:对于取值不能重复,经常作为查询条件的字段,应该建唯一索引(主键默认唯一索引),并且将查询条件中该字段的条件置于第一个位置。没有必要再建立与该字段有关的联合索引。
规则12:对于经常查询的字段,其值不唯一,也应该考虑建立普通索引,查询语句中该字段条件置于第一个位置,对联合索引处理的方法同样。
规则13:业务通过不唯一索引访问数据时,需要考虑通过该索引值返回的记录稠密度,原则上可能的稠密度最大不能高于0.2,如果稠密度太大,则不合适建立索引了。
规则14:需要联合索引(或联合主键)的数据库要注意索引的顺序。SQL语句中的匹配条件也要跟索引的顺序保持一致。注意:索引的顺势不正确也可能导致严重的后果。
规则15:表中的多个字段查询作为查询条件,不含有其他索引,并且字段联合值不重复,可以在这多个字段上建唯一的联合索引,假设索引字段为 (a1,a2,...an),则查询条件(a1 op val1,a2 op val2,...am op valm)m<=n,可以用到索引,查询条件中字段的位置与索引中的字段位置是一致的。
规则16:联合索引的建立原则(以下均假设在数据库表的字段a,b,c上建立联合索引(a,b,c))
1、联合索引中的字段应尽量满足过滤数据从多到少的顺序,也就是说差异最大的字段应该房子第一个字段
2、建立索引尽量与SQL语句的条件顺序一致,使SQL语句尽量以整个索引为条件,尽量避免以索引的一部分(特别是首个条件与索引的首个字段不一致时)作为查询的条件
3、Where a=1,where a>=12 and a<15,where a=1 and b<5 ,where a=1 and b=7 and c>=40为条件可以用到此联合索引;而这些语句where b=10,where c=221,where b>=12 and c=2则无法用到这个联合索引。
4、当需要查询的数据库字段全部在索引中体现时,数据库可以直接查询索引得到查询信息无须对整个表进行扫描(这就是所谓的key-only),能大大的提高查询效率。
当a,ab,abc与其他表字段关联查询时可以用到索引
5、当a,ab,abc顺序而不是b,c,bc,ac为顺序执行Order by或者group不要时可以用到索引
6、以下情况时,进行表扫描然后排序可能比使用联合索引更加有效
a.表已经按照索引组织好了
b.被查询的数据站所有数据的很多比例。
规则17:重要业务访问数据表时。但不能通过索引访问数据时,应该确保顺序访问的记录数目是有限的,原则上不得多于10.
二.Query语句与应用系统优化
规则18:合理构造Query语句
1、Insert语句中,根据测试,批量一次插入1000条时效率最高,多于1000条时,要拆分,多次进行同样的插入,应该合并批量进行。注意query语句的长度要小于mysqld的参数 max_allowed_packet
2、查询条件中各种逻辑操作符性能顺序是and,or,in,因此在查询条件中应该尽量避免使用在大集合中使用in (or in 到底那个性能更好,很多博客上写的都不一样)
3、永远用小结果集驱动大记录集,因为在mysql中,只有Nested Join
一种Join方式,就是说mysql的join是通过嵌套循环来实现的。通过小结果集驱动大记录集这个原则来减少嵌套循环的循环次数,以减少IO总量及CPU运算次数
4、尽量优化Nested Join内层循环。
5、只取需要的columns,尽量不要使用select *
6、仅仅使用最有效的过滤字段,where 字句中的过滤条件少为好
7、尽量避免复杂的Join和子查询
Mysql在并发这块做得并不是太好,当并发量太高的时候,整体性能会急剧下降,这主要与Mysql内部资源的争用锁定控制有关,MyIsam用表锁,InnoDB好一些用行锁
规则19:应用系统的优化
1、合理使用cache,对于变化较少的部分活跃数据通过应用层的cache缓存到内存中,对性能的提升是成数量级的。
2、对重复执行相同的query进行合并,减少IO次数。
3、事务相关性最小原则
以上是关于SQL优化方法的主要内容,如果未能解决你的问题,请参考以下文章
优化 C# 代码片段、ObservableCollection 和 AddRange