原文出处:http://blog.csdn.net/dba_huangzj/article/details/7623926
SQL查询性能的好坏直接影响到整个数据库的价值,对此,必须郑重对待。sql
SQL Server提供了多种工具,下面作一个简单的介绍:数据库
1、SQL Profiler工具缓存
SQL Profiler可用于:安全
l 图形化监视SQLServer查询;服务器
l 在后台收集查询信息;函数
l 分析性能;工具
l 诊断像死锁这样的问题;性能
l 调试Transact-SQL(T-SQL)语句;学习
l 模拟重放SQLServer活动大数据
注意:定义一个跟踪最有效的方法是经过系统存储过程,可是学习的起点仍是经过GUI。
1.一、 Profiler跟踪:
建议使用标准模版
1.二、 事件:
一个事件表现SQLServer中执行的各类活动。能够简单分类为:事件类、游标事件、锁事件、存储过程事件和T-SQL事件。
对于性能分析,主要关心如下部分:
l SQL活动涉及哪一类的CPU使用?
l 使用了多少内存?
l 涉及多少I/O操做?
l SQL活动执行了多长时间?
l 特定的查询执行的频率多高?
l 查询面对哪类错误和警告?
跟踪查询结束的事件:
事件类 |
事件 |
描述 |
Stored Procedures |
RPC:Completed |
RPC完成事件 |
SP:Completed |
存储过程完成事件 |
|
SP:StmtCompleted |
在存储过程当中一条SQL语句完成事件 |
|
TSQL |
SQL:BatchCompleted |
T-SQL批完成事件 |
SQL:StmtCompleted |
一条T-SQL语句完成事件 |
RPC事件表示存储过程使用远程过程调用(RPC)机制经过OLEDB命令执行。若是一个数据库应用程序使用T-SQL EXECUTE语句执行一个存储过程,那么会被转化为一个SQL批而不是一个RPC,RPC一般比EXECUTE请求快,由于它们绕过了SQLServer中的许多语句解析和参数处理。
T-SQL批是一组被一块儿提交到SQLServer的SQL查询,以GO结束。GO不是一条T-SQL语句,而是有Sqlcmd使用程序和Management Studio识别。象征着批的结束。T-SQL批由一条或多条T-SQL语句组成。语句或T-SQL语句在存储过程(如下简称SP)中也是独立和离散的。用SP:StmtCompleted或SQL:StmtCompleted事件捕获单独的语句可能代价很高。收集时要很是谨慎,特别在生产环境上。
跟踪查询性能的事件:
事件类 |
事件 |
描述 |
Security Audit(安全审计) |
Audit Login(登陆审计) |
记录用户链接到SQL Server或断开链接时数据库的链接 |
Audit Logou(注销审计) |
||
Seesions(会话) |
ExistingConnection(现有链接) |
表示全部在跟踪开始以前链接到SQLServer的用户 |
Cursors(游标) |
CursorImplicitConversion(游标隐含转换) |
代表建立的游标类型与所请求的类型不一样。 |
Errors and Warnings(错误和警告) |
Attention(注意) |
表示因为客户撤销查询或者数据库链接破坏引发的请求中断 |
Exception(异常) |
代表SQLServer中发生了异常 |
|
Execution Warnings(执行警告) |
代表在查询或SP执行过程当中出现了警告 |
|
Hash Warning(hash警告) |
代表hash操做中发生了错误 |
|
Missing Column Statistics(列统计丢失) |
代表优化器要求的肯定处理策略用的列统计丢失。 |
|
Missing Join Predicate(链接断言丢失) |
代表查询在两表之间没有链接断言状况下执行。 |
|
Sort Warnings(排序警告) |
代表像select这样的查询中执行的排序操做没有合适的内存。 |
|
Locks(锁) |
Lock: Deadlock(死锁) |
标志着死锁的出现 |
Lock: Deadlock Chain(死锁链) |
显示产生死锁的查询链条 |
|
Lock: Timeout(锁超时) |
表示锁已经超过了其超时参数,该参数由SET LOCK_TIMEOUT timeout_period(MS)命令设置 |
|
Stored Procedures(存储过程) |
SP:Recompile(重编译) |
代表用于一个存储过程的执行计划必须重编译,缘由是执行计划不存在,强制的重编译,或者现有的执行计划不能重用。 |
SP:Starting(开始) SP:StmtStarting(语句开始) |
分别表示一个SP:StmtStarting存储过程和存储过程当中的一条SQL语句的开始。它们对于识别开始但由于一个操做致使Attention事件而未能结束的查询颇有用。 |
|
Transactions(事务) |
SQLTransaction(SQL事务) |
提供数据库事务的信息,包括事务开始/结束的时间、事务持续时间的信息。 |
1.三、 数据列:事件的特性。如事件的类、用于该事件的SQL语句、锁资源开销及事件来源。
数据列 |
描述 |
EventClass(事件类) |
事件类型,如SQL:StatementCompleted |
TextData |
事件所用的SQL语句 |
CPU |
事件的CPU开销(ms) |
Reads |
为一个事件所执行的逻辑读操做数量。 |
Writes |
一个事件所执行的逻辑写操做数量。 |
Duration |
事件的执行事件(ms) |
SPID |
该事件的进程ID |
StratTime |
事件开始的事件 |
逻辑读、写由内存中的8KB页面活动组成,可能须要0或者多个物理I/O。找到物理I/O操做数,使用系统监视工具。
2、跟踪的自动化
注意:SQL Profiler对性能存在负面影响,如非必要不要在生产环境长期使用。
1. 使用GUI捕捉跟踪:
可使用两种方法建立脚本化的跟踪——手工或GUI:
可使用Profiler的导出功能导出脚本。
2. 使用存储过程捕捉跟踪:
l Sp_trace_create:建立一个跟踪定义。
l Sp_trace_setevent:添加事件和事件列到跟踪中。
l Sp_trace_setfilter:将过滤器应用到跟踪。
可使用内建函数:fn_trace_getinfo肯定正在运行的跟踪:
SELECT * FROM ::fn_trace_getinfo(default);
可使用:sp_trace_setstatus中止特定的跟踪:
EXEC sp_trace_setstatus 1,0
关闭跟踪后,必须删除:
EXEC sp_trace_setstatus 1,2
能够从新执行fn_trace_getinfo函数确认是否已经关闭。
3、结合跟踪和性能监视器输出
能够结合SQL Profiler和性能监视器来分析性能,此处很少说
4、SQL Profiler建议
使用SQL Profiler时,要考虑如下几点:
l 限制事件和数据列的数量;
l 抛弃用于性能分析的启动事件;
l 限制跟踪输出大小;
l 避免联机数据列排序;
l 远程运行Profiler
一、 限制事件和数据列:
捕捉像锁和执行计划这样的事件时应该当心进行,由于输出会变得很是大并下降SQL Server性能。
二、 丢弃性能分析所用的启动事件:
像SP:StmtStarting这样的启动事件不提供分析信息,由于只有事件完成才能计算I/O量、CPU负载和查询的持续时间。
使用捕捉启动事件的时机是:预期某些SQL查询由于错误而不能结束执行,或者频繁发现Attention事件按的时候捕捉。由于Attention事件通常表示用户中途撤销了查询或者查询超时,可能由于查询运行了太长时间。
三、 限制跟踪输出大小:
在Edit Filter(编辑过滤器)对话框中作如下设置:
l Duration-Greater than or equal:2(持续事件>=2):持续事件等于0或1ms的查询不能进一步优化。
l Reads-Greater than or equal:2(读操做数量>=2):逻辑读数量等于0或1的查询不能进一步优化。
四、 避免在线数据列排序:
(1)、捕捉跟踪,不作任何排序或分组。
(2)、保存跟踪输出到一个跟踪文件。
(3)、打开跟踪文件并按照须要排序。
五、 远程运行Profiler:
使用系统存储过程比使用GUI对性能方面有好处。
六、 限制使用某些事件:在已经遇到压力的系统上,不要使用Showplan XML事件
5、没有Profiler状况下的查询性能度量
对于须要当即捕捉系统,使用DMV:sys.dm_exec_query_stats比Profiler有效,若是须要查询运行机器单独开销的历史记录,跟踪还是更好的工具。
sys.dm_exec_query_stats:获取服务器上查询计划统计的信息:
列 |
描述 |
Plan_handle |
引用执行计划的指针 |
Creation_time |
计划建立的时间 |
Last_execution time |
查询最后一次使用计划的时间 |
Execution_count |
计划已经使用的次数 |
Total_worker_time |
从建立起计划使用的CPU时间 |
Total_logical_reads |
从建立起计划使用的读操做数量 |
Total_logical_writes |
从建立起计划使用的写操做数量 |
Query_hash |
可用于识别有相似逻辑的查询的一个二进制hash |
Query_plan_hash |
可用于识别有类似逻辑的计划的一个二进制hash |
为了过滤信息,须要关联其余DMF。如sys.dm_exec_sql_text来查看查询文本。
Sys.dm_query_plan显示查询的执行计划。从而限制没必要要的返回信息。
6、开销较大的查询
对于收集结果,应该分析两部分:
l 致使大量系统资源压力的查询;
l 速度下降最严重的查询
一、 识别开销较大的查询:
对于返回的跟踪数据,CPU和Reads列显示了查询开销所在。在执行读操做时,内存页面必须在操做查询中被备份,在第一次数据访问期间写入,并在内存瓶颈时被移到磁盘。过多页面CPU还会增长管理页面的负担。
致使大量逻辑读的查询一般在相应的大数据集上获得锁。即便读,也须要在全部数据上的共享锁。阻塞了其余请求修改的查询。但不阻塞读数据的查询。若是查询好久,那么会持续阻塞其余查询,被阻塞的查询进一步阻塞其余查询,引发数据中的阻塞链。
结论,识别开销大的查询并首先优化它们从而达到如下效果:
l 增进开销较大的查询自己的性能;
l 下降系统资源上的整体压力;
l 减小数据库阻塞;
开销大的查询有两类:
l 单次执行:查询一次开销较大
l 屡次执行:查询自己不大,可是重复执行致使系统资源上的压力。
1. 单次执行开销较大的查询:
可使用SQL Profiler,或者查询sys.dm_exec_query_stats来识别开销大的查询。
(1)、捕捉表示典型工做负载的Profiler跟踪。
(2)、将跟踪输出保存到一个跟踪文件。
(3)、打开跟踪文件进行分析。
(4)、打开跟踪的Properties(属性)窗口,单击Event Selection(事件选择)选项卡。
(5)、单机按钮打开Organize Columns(组织列)窗口。
(6)、在Reads列上分组跟踪输出。
(7)、使用分组的跟踪。
2. 屡次执行开销较大的查询:
l 这种状况下,Profiler中跟踪输出的如下列上分组:EventClass、TextData和Reads。
l 导出Profiler跟踪表。使用内建函数fn_trace_gettable导入到一个跟踪表。
l 访问sys.dm_exec_query_statsDMV从生产服务器检索信息。
把数据装入到数据库的一个表中
SELECT * INTO Trace_Table FROM :: FN_TRACE_GETTABLE('C:\PerformanceTrace.trc', DEFAULT)
执行下面语句查询屡次执行的读操做总数:
SELECT COUNT(*) AS TotalExecutions , EventClass , TextData , SUM(Duration) AS Duration_Total , SUM(CPU) AS CPU_Total , SUM(Reads) AS Reads_Total , SUM(Writes) AS Writes_Total FROM Trace_Table GROUP BY EventClass , TextData ORDER BY Reads_Total DESC
SQL Server 2008不支持在NTEXT数据类型进行分组。而TextData是ntext类型,要转换成Nvarchar(max)
SELECT ss.sum_execution_count , t.text , ss.sum_total_elapsed_time , ss.sum_total_worker_time , ss.sum_total_logical_reads , ss.sum_total_logical_writes FROM ( SELECT s.plan_handle , SUM(s.execution_count) sum_execution_count , SUM(s.total_elapsed_time) sum_total_elapsed_time , SUM(s.total_worker_time) sum_total_worker_time , SUM(s.total_logical_reads) sum_total_logical_reads , SUM(s.total_logical_writes) sum_total_logical_writes FROM sys.dm_exec_query_stats s GROUP BY s.plan_handle ) AS ss CROSS APPLY sys.dm_exec_sql_text(ss.plan_handle) t ORDER BY sum_total_logical_readsDESC
3. 识别运行缓慢的查询:
须要按期监视输入的SQL查询的执行时间,并找出运行缓慢的查询的响应时间。可是不是全部运行缓慢的查询都是因为资源问题造成。如阻塞那些都有可能致使缓慢的查询。
能够在Duration上跟踪。
7、执行计划
一、 分析查询计划
执行计划从右到左,从上到下的顺序阅读。每一个步骤表明得到查询最终输出所执行的操做。执行计划有如下特征:
l 若是查询由多个查询的批组成,每一个查询的执行计划按照执行的顺序显示。批中的每一个执行将有一个相对的估算开销,整个批的总开销为100%。
l 执行计划中的每一个图标表明一个操做符。有相对的估算开销,全部节点的总开销为100%。
l 执行计划中的一个起始操做符一般表示一个数据库对象(表或索引)的数据检索机制。
l 数据检索一般是一个表操做或索引操做。
l 索引上的数据检索将是索引扫描或索引查找。
l 索引上的数据检索的命名惯例是[表名].[索引名]。
l 数据从右到左在两个操做之间流动,由一个链接箭头表示。
l 操做符之间链接箭头的宽度是传输行数的图形表示。
l 同一列的两个操做符之间的链接机制将是嵌套的循环链接,hash匹配链接或者合并链接。
l 将光标放置在执行计划的一个节点上,显示一个具备一些细节的弹出窗口。
l 在Properties(属性)窗口中有完整的一组关于操做符的细节。能够右键单击操做符并选择Properties。
l 操做符细节在顶部显示物理和逻辑操做的类型。物理操做表明存储引擎实际使用的,而逻辑操做是优化器用于创建估算执行计划的结构。若是相同,只显示物理操做。还会显示其余信息:I/O、CPU等。
l 操做符细节弹出窗口的Argument(参数)部分在分析中特别有用,由于显示了优化器锁使用的过滤或链接条件。
二、 识别执行计划中开销较大的步骤:
l 执行计划中每一个节点显示整个计划中的相对开销,整个计划总开销为100%。关注最高相对开销的节点。
l 执行计划可能来自于一批语句,所以可能也须要查找开销最大的语句。
l 查看节点之间链接箭头的宽度。很是宽的链接箭头表示对应节点之间的传输大量的行。分析箭头左边的节点以理解须要这么多行的缘由,还要检查箭头的属性。可能看到估计的行和实际的行不同,这可能由过期的统计形成。
l 寻找hash链接操做。对于小的数据集,嵌套的循环链接一般是首选的链接技术。
l 寻找书签查找操做。对于大结果集的书签操做可能形成大量的逻辑读。
l 若是操做符上有一个叹号的警告,是须要马上注意的领域。这些警告多是由各类问题形成的,包括没有链接条件的链接或者丢失统计的索引和表。
l 需找执行排序操做的步骤,这表示数据没有以正确的排序进行检索。
三、 分析索引有效性:
要关注【扫描】,扫描表明访问大量的行。能够经过如下方式判断索引有效性:
l 数据检索操做
l 链接操做
有时候执行计划中没有【断言】(predicate),缺少断言意味着整个表(聚簇索引就是该表)被做为合并链接操做符的输入进行扫描。
四、 分析链接有效性:
SQLServer使用3中链接类型:
l Hash链接;
l 合并链接
l 嵌套循环链接
一、 Hash链接:
1.一、 Hash链接高效处理大的、未排序的、没有索引的输入。
1.二、 Hash链接使用两个链接输入:创建输入(build input)和探查输入(probe input)。创建输入是执行计划中上面的那个输入,探查输入是下面那个输入。
1.三、 最多见的hash链接方式——in-memory hash join,整个创建输入被扫描或计算而后在内存中创建一个hash表。每一个行根据计算的hash键值(相等断言中的一组列)被插入一个hash表元中。
内存hash链接的示意图:
二、 合并链接:
2.一、合并链接要求两个输入在合并列上排序,这将在链接条件中定义。若是两个链接有索引,那么链接输入由该索引排序。因为每一个链接输入都被排序了,合并排序从每一个输入获得一行并比较是否相等。若是相等,匹配行被生成。过程被重复到全部行都被处理。
2.二、若是优化器发现链接输入都在其链接列上排序,合并链接就比hash链接更快而被选中。
三、 嵌套循环链接:
3.一、始终从单独的表中访问有限数量的行,为了理解使用较小结果集的效果,在查询中下降链接输入。
3.二、使用一个链接输入做为外部(outer)输入表。另外一个做为内部(inner)输入表。外部表是执行计划的上方输入,内部表是下方输入。外部循环逐行消费外部输入表。内部循环为每一个外部行执行一次,搜索内部输入表的匹配行。
3.三、若是外部输入至关小,内部输入大但有索引,嵌套循环链接是很是高效的。链接经过牺牲其余方面来提升速度——使用内存来取得小的数据集并快速与第二个数据集比较。合并排序与此相似,使用内存和一小部分tempdb排序,hash链接使用内存和tempdb创建hash表。
3.四、虽然循环链接更快,可是随着数据集变得更大,比hash或合并消耗更多的内存。因此SQL Server会在不一样数据集的状况下使用不一样计划的缘由。
3种链接类型的特性:
链接类型 |
链接列上的索引 |
链接表的通常大小 |
预先排序 |
链接子句 |
Hash |
内部表:不须要索引 外部表:可选 最佳条件:小的外部表,大的内部表 |
任意 |
不须要 |
Equi-join |
合并 |
内部/外部表:必须 最佳条件:两个表都有聚簇索引或覆盖索引 |
大 |
须要 |
Equi-join |
嵌套循环 |
内部表:必须 外部表:最好有 |
小 |
可选 |
全部 |
注意:在hash和嵌套循环链接中,外部表通常是两个链接表中较小的一个。
五、 实际执行计划vs估算执行计划:
估算执行计划对临时表没法生成。
六、 计划缓存:
通常是保存在内存空间。可使用DMV来查询:
SELECT p.query_plan , t.text FROM sys.dm_exec_cached_plansr CROSS APPLY sys.dm_exec_query_plan(r.plan_handle) p CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) t
8、查询开销
一、 客户统计:将计算机做为服务器的一个客户端,从这个角度去发出捕捉执行信息。
点击SSMS中的【查询】→【包含客户统计】,但这一步不是很好的收集方法。有时候须要重置:【查询】→【重置客户统计】
二、 执行时间:
Duration和CPU都表明着查询的时间因素,可使用SET STATISTICS TIME来取得执行时间。
其中最后一行的CPU时间等于Profiler的CPU值,占用时间表明Duration值。0毫秒的分析和编译时间说明重用了执行计划。能够执行:DBCC FREEPROCCACHE清除缓存。可是不要在生产系统上执行,由于某种状况下,这和重启的开销相同。
三、 STATISTICS IO:
Profiler获取的Reads列的读取次数尝尝是Duration、CPU、Reads和Writes这些因素中最重要的。在解读STATISTICS IO的输出时,多半参考【逻辑读】操做。有时候也会参考扫描计数。物理读操做和预读数量在数据不能在内存中找到时将不为0,但一旦数据填写到内存,物理读和预读将趋向于0。在优化期间,能够监控单表的读操做次数以确保确实减小了该表的数据访问开销。