前一段时间一直在优化系统,看了一些关于SQL语句优化的东西,在这里分享一下。
1、统一SQL语句的写法
对于下面两句SQL语句。程序猿觉得是同样的。数据库查询优化器觉得是不同的。
select*from dual
select*From dual
事实上就是大写和小写不同,查询分析器就觉得是两句不同的SQL语句,必须进行两次解析。生成2个运行计划。
所以作为程序猿,应该保证同样的查询语句在不论什么地方都一致,多一个空格都不行!
2、使用“暂时表”暂存中间结果
简化SQL语句的重要方法就是採用暂时表暂存中间结果,可是,暂时表的优点远远不止这些。将暂时结果暂存在暂时表,后面的查询就在tempdb中了,这能够避免程序中多次扫描主表,也大大降低了程序运行中“共享锁”堵塞“更新锁”,降低了堵塞,提高了并发性能。
3、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语句的负担。
一次解析,多次重用,是提高数据库效率的原则。
4、仅仅在必要的情况下才使用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。
5、使用like进行模糊查询时应注意
有的时候会须要进行一些模糊查询比方
select*from contact where username like ‘%yue%’
关键词%yue%,由于yue前面用到了“%”,因此该查询必定走全表扫描,除非必要,否则不要在关键词前加%,
6、
• 确保TIMED_STATISTICS在实例级设置为TRUE。
• 确保MAX_DUMP_FILE_SIZE设置为足够大的值。
• 确定指向USER_DUMP_DEST的位置。并确保有足够大的值。
• 为正在被讨论的会话开启SQL_TRACE。
• 运行应用程序。
• 确定跟踪文件的位置。
• 在步骤6所确定的跟踪文件上运行tkprof以产生跟踪输出文件。
• 研究跟踪输出文件。
• 优化最昂贵的SQL语句。
• 重复运行步骤4到步骤9。直到所需的性能目标达到为止。
TIMED_STATISTICS 作用
1、仅仅统计wait event的等待的次数(TIMED_STATISTICS为false)
2、统计wait event的等待的时间和次数(TIMED_STATISTICS为true)
MAX_DUMP_FILE_SIZE參数:限制trace files及alert file大小
SQL语句的优化是在开发过程中的,那么假设发现系统运行中哪些SQL语句是影响性能须要改变的呢?
1、SQL SERVER 2005的性能工具中有SQL Server Profiler和数据库引擎优化顾问,极好的东东,必须熟练使用。
2、查询SQL语句时打开“显示预计的运行计划”。分析每一个步骤的情况
3、0基础做法,在CPU占用率高的时候,打开SQL Server Profiler运行。将跑下来的数据存到文件里,然后打开数据库引擎优化顾问调用那个文件进行分析。由SQL SERVER提供索引优化建议。採纳它的INDEX索引优化部分。
4、但上面的做法常常不会跑出你所须要的,在近期的优化过程中CPU占用率极高。但根本提不出我须要的优化建议。特别是有些语句是在存储过程中并且多表联立。
这时就须要用中级做法来定位占用CPU高的语句。
5、还是运行SQL Server Profiler,将运行结果保存到某个库的新表中(随便起个名字系统会自己建)。让它运行一段时间,然后能够用
select top 100 * from test where textdata is not null order by duration desc
这个能够选出运行时间长的语句。在ORDER BY 中能够替换成CPU、READS,来选出CPU占用时间长和读数据过多的语句。
定位出问题的语句之后就能够详细分析了。有些语句在运行计划中非常明显能够看出问题所在。
常见的有没有建索引或索引建立不合理,会出现table scan或index scan。凡是看到SCAN。就意味着会做全表或全索引扫描,这是带来的必定是读次数过多。
我们期望看到的是seek或键查找。
6、 怎么看SQL语句运行的计划非常有讲究,刚開始学习的人会过于关注里面显示的开销比例,而实际上这个有时会误导。我在实际优化过程中就被发现。一个index scan的运行项开销仅仅占25%,还有一个键查找的开销占50%,而键查找部分根本没有可优化的,SEEK谓词就是ID=XXX这个建立在主键上的查找。而 细致分析能够看到。后者CPU开销0.00015,I/O开销0.0013。而前者呢,CPU开销1.4xxxx,I/O开销也远大于后者。因此,优化重 点应该放在前者。
7、怎样优化单个部分,一个复杂的SQL语句。SQL SERVER会非常聪明地重组WHERE后的语句,试图匹配索引。选中带优化的步骤,选择旁边的‘属性”,再选择当中的“谓词”,将当中部分复制下来,这部 分就是分解后的WHERE 语句,然后在查询界面中select * from 表 where 刚才复制下来的“谓词”。这个就是须要优化的部分。既然已经走到这一步了。大部分人应该能手动建立索引了,由于这里的WHERE语句比之前的肯定简单不 少。(在我项目中原始SELECT语句的WHERE部分有10个条件组合,涉及6个字段,提取出来要优化的部分就4个条件。涉及到3个字段。新的索引建立 后。CPU占用率一下子就降低了。并且新建立的索引涉及的字段属于不常UPDATE的部分,频繁的读写操作不会影响UPDATE的效率)
8、以上就是优化的思路。最后提一些优化过程或是系统设计时中须要注意的问题。
A、尽量避免用select * from xxx where abc like ‘%xxx’类型的模糊查询。由于%在前面的话是无法利用到索引。必定会引起全量SCAN操作。应该找寻替代方式或用前置条件语句把like查找之前的行数减到最低。
B、 尽量避免对大表数据进行select top n * from xxx where xxxx order by newid()的取随机记录的操作。newid()操作会读全量数据后再排序。也会占用大量CPU和读操作。
能够考虑用RAND()函数来实现,这方面我 还在研究中。对于整表操作比較好弄。比方id>=(select max(id) from table)*rand()。但假设取局部数据的随机记录还须要思量。
C、在SQL Server Profiler记录中会看到Audit Logout会占用大量CPU和读写等操作。查了一些资料称是某个链接在某次连接过程中运行SQL语句产生的总数,不用过于操心。看下来的确似乎这样,非常 多Audit Logout的CPU和IO消耗量和之前优化的语句基本一致。所以在第5点我提的SQL语句用textdata is not null条件把Audit Logout给隐去。
D、两个不同字段OR语句会导致全表扫描。
比如 where m=1 or n=1。假设建立一个索引是m和n,同样会引起scan,解决方法是给m和n分别建立索引。
測试12万条数据的表,索引建立错误的情况下IO开销高达 10.xxx。分别建立索引后。所有变成0.003,这个反差是非常巨大的。尽管会引起INSERT操作的性能问题。但毕竟大部分瓶颈在SELECT的读 操作上。
E、索引查找(Index Seek)和索引扫描(Index Scan),我们须要的是前者,而引起后者的原因一般是某个索引里的字段多余要查找的,比如索引建立在A和B两个字段,而我们仅仅要查找A,则会导致 INDEX SCAN。建议针对单独的A建立索引,以形成索引查找。
F、对于小表不建议建立索引。特别是几百的数据量,仅仅有上千上万级别的数据建立索引才有效果。
数据库优化是非常深的学问,在数据库设计时就应该注意,特别是最后提到的A、B两点,尽可能在设计初期避免。 一般不要用例如以下的字句: “IS NULL”, “<>”, “!=”, “!>”, “!<”, “NOT”, “NOT EXISTS”, “NOT IN”, “NOT LIKE”, and “LIKE ‘%500’”,由于他们不走索引全是表扫描。Select COUNT(*)的效率教低