一、 首先要搞明白什么叫执行计划?html
执行计划是数据库根据SQL语句和相关表的统计信息做出的一个查询方案,这个方案是由查询优化器自动分析产生的,好比一条SQL语句若是用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,若是该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式。程序员
可见,执行计划并非固定的,它是“个性化的”。产生一个正确的“执行计划”有两点很重要:sql
(1) SQL语句是否清晰地告诉查询优化器它想干什么?数据库
(2) 查询优化器获得的数据库统计信息是不是最新的、正确的?并发
二、 统一SQL语句的写法oracle
对于如下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不一样的。高并发
select*from dualoop
select*From dual性能
其实就是大小写不一样,查询分析器就认为是两句不一样的SQL语句,必须进行两次解析。生成2个执行计划。因此做为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行!优化
三、 不要把SQL语句写得太复杂
我常常看到,从数据库中捕捉到的一条SQL语句打印出来有2张A4纸这么长。通常来讲这么复杂的语句一般都是有问题的。我拿着这2页长的SQL语句去请教原做者,结果他说时间太长,他一时也看不懂了。可想而知,连原做者都有可能看糊涂的SQL语句,数据库也同样会看糊涂。
通常,将一个Select语句的结果做为子集,而后从该子集中再进行查询,这种一层嵌套语句仍是比较常见的,可是根据经验,超过3层嵌套,查询优化器就很容易给出错误的执行计划。由于它被绕晕了。像这种相似人工智能的东西,终究比人的分辨力要差些,若是人都看晕了,我能够保证数据库也会晕的。
另外,执行计划是能够被重用的,越简单的SQL语句被重用的可能性越高。而复杂的SQL语句只要有一个字符发生变化就必须从新解析,而后再把这一大堆垃圾塞在内存里。可想而知,数据库的效率会何等低下。
四、 使用“临时表”暂存中间结果
简化SQL语句的重要方法就是采用临时表暂存中间结果,可是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这能够避免程序中屡次扫描主表,也大大减小了程序执行中“共享锁”阻塞“更新锁”,减小了阻塞,提升了并发性能。
五、 OLTP系统SQL语句必须采用绑定变量
select*from orderheader where changetime >'2010-10-20 00:00:01'
select*from orderheader where changetime >'2010-09-22 00:00:01'
以上两句语句,查询优化器认为是不一样的SQL语句,须要解析两次。若是采用绑定变量
select*from orderheader where changetime >@chgtime
@chgtime变量能够传入任何值,这样大量的相似查询能够重用该执行计划了,这能够大大下降数据库解析SQL语句的负担。一次解析,屡次重用,是提升数据库效率的原则。
六、 绑定变量窥测
事物都存在两面性,绑定变量对大多数OLTP处理是适用的,可是也有例外。好比在where条件中的字段是“倾斜字段”的时候。
“倾斜字段”指该列中的绝大多数的值都是相同的,好比一张人口调查表,其中“民族”这列,90%以上都是汉族。那么若是一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。这个时候若是采用绑定变量@nation会存在很大问题。
试想若是@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。而后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。可是,因为重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。
七、 只在必要的状况下才使用begin tran
SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,比如在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。
有些状况下,咱们须要显式声明begin tran,好比作“插、删、改”操做须要同时修改几个表,要求要么几个表都修改为功,要么都不成功。begin tran 能够起到这样的做用,它能够把若干SQL语句套在一块儿执行,最后再一块儿commit。好处是保证了数据的一致性,但任何事情都不是天衣无缝的。Begin tran付出的代价是在提交以前,全部SQL语句锁住的资源都不能释放,直到commit掉。
可见,若是Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交以前,必然会阻塞别的语句,形成block不少。
Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些状况下能够采用触发器同步数据,不必定要用begin tran。
八、 一些SQL查询语句应加上nolock
在SQL语句中加nolock是提升SQL Server并发性能的重要手段,在oracle中并不须要这样作,由于oracle的结构更为合理,有undo表空间保存“数据前影”,该数据若是在修改中还未commit,那么你读到的是它修改以前的副本,该副本放在undo表空间中。这样,oracle的读、写能够作到互不影响,这也是oracle 广受称赞的地方。SQL Server 的读、写是会相互阻塞的,为了提升并发性能,对于一些查询,能够加上nolock,这样读的时候能够容许写,但缺点是可能读到未提交的脏数据。使用 nolock有3条原则。
(1) 查询的结果用于“插、删、改”的不能加nolock !
(2) 查询的表属于频繁发生页分裂的,慎用nolock !
(3) 使用临时表同样能够保存“数据前影”,起到相似oracle的undo表空间的功能,
能采用临时表提升并发性能的,不要用nolock 。
九、 汇集索引没有建在表的顺序字段上,该表容易发生页分裂
好比订单表,有订单编号orderid,也有客户编号contactid,那么汇集索引应该加在哪一个字段上呢?对于该表,订单编号是顺序添加的,若是在orderid上加汇集索引,新增的行都是添加在末尾,这样不容易常常产生页分裂。然而,因为大多数查询都是根据客户编号来查的,所以,将汇集索引加在contactid上才有意义。而contactid对于订单表而言,并不是顺序字段。
好比“张三”的“contactid”是001,那么“张三”的订单信息必须都放在这张表的第一个数据页上,若是今天“张三”新下了一个订单,那该订单信息不能放在表的最后一页,而是第一页!若是第一页放满了呢?很抱歉,该表全部数据都要日后移动为这条记录腾地方。
SQL Server的索引和Oracle的索引是不一样的,SQL Server的汇集索引其实是对表按照汇集索引字段的顺序进行了排序,至关于oracle的索引组织表。SQL Server的汇集索引就是表自己的一种组织形式,因此它的效率是很是高的。也正由于此,插入一条记录,它的位置不是随便放的,而是要按照顺序放在该放的数据页,若是那个数据页没有空间了,就引发了页分裂。因此很显然,汇集索引没有建在表的顺序字段上,该表容易发生页分裂。
曾经碰到过一个状况,一位哥们的某张表重建索引后,插入的效率大幅降低了。估计状况大概是这样的。该表的汇集索引可能没有建在表的顺序字段上,该表常常被归档,因此该表的数据是以一种稀疏状态存在的。好比张三下过20张订单,而最近3个月的订单只有5张,归档策略是保留3个月数据,那么张三过去的 15张订单已经被归档,留下15个空位,能够在insert发生时从新被利用。在这种状况下因为有空位能够利用,就不会发生页分裂。可是查询性能会比较低,由于查询时必须扫描那些没有数据的空位。
重建汇集索引后状况改变了,由于重建汇集索引就是把表中的数据从新排列一遍,原来的空位没有了,而页的填充率又很高,插入数据常常要发生页分裂,因此性能大幅降低。
对于汇集索引没有建在顺序字段上的表,是否要给与比较低的页填充率?是否要避免重建汇集索引?是一个值得考虑的问题!
十、加nolock后查询常常发生页分裂的表,容易产生跳读或重复读
加nolock后能够在“插、删、改”的同时进行查询,可是因为同时发生“插、删、改”,在某些状况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,好比在第100页已经读过的记录,可能会由于页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。同理,若是在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。
上面提到的哥们,在加了nolock后一些操做出现报错,估计有可能由于nolock查询产生了重复读,2条相同的记录去插入别的表,固然会发生主键冲突。
十一、使用like进行模糊查询时应注意
有的时候会须要进行一些模糊查询好比
select*from contact where username like ‘%yue%’
关键词%yue%,因为yue前面用到了“%”,所以该查询必然走全表扫描,除非必要,不然不要在关键词前加%,
十二、数据类型的隐式转换对查询效率的影响
sql server2000的数据库,咱们的程序在提交sql语句的时候,没有使用强类型提交这个字段的值,由sql server 2000自动转换数据类型,会致使传入的参数与主键字段类型不一致,这个时候sql server 2000可能就会使用全表扫描。Sql2005上没有发现这种问题,可是仍是应该注意一下。
1三、SQL Server 表链接的三种方式
(1) Merge Join
(2) Nested Loop Join
(3) Hash Join
SQL Server 2000只有一种join方式——Nested Loop Join,若是A结果集较小,那就默认做为外表,A中每条记录都要去B中扫描一遍,实际扫过的行数至关于A结果集行数x B结果集行数。因此若是两个结果集都很大,那Join的结果很糟糕。
SQL Server 2005新增了Merge Join,若是A表和B表的链接字段正好是汇集索引所在字段,那么表的顺序已经排好,只要两边拼上去就好了,这种join的开销至关于A表的结果集行数加上B表的结果集行数,一个是加,一个是乘,可见merge join 的效果要比Nested Loop Join好多了。
若是链接的字段上没有索引,那SQL2000的效率是至关低的,而SQL2005提供了Hash join,至关于临时给A,B表的结果集加上索引,所以SQL2005的效率比SQL2000有很大提升,我认为,这是一个重要的缘由。
总结一下,在表链接时要注意如下几点:
(1) 链接字段尽可能选择汇集索引所在的字段
(2) 仔细考虑where条件,尽可能减少A、B表的结果集
(3) 若是不少join的链接字段都缺乏索引
原文连接:http://www.cnblogs.com/ATree/archive/2011/02/13/sql_optimize_1.html