本文属于《理解性能的奥秘——应用程序中慢,SSMS中快》系列
接上文:理解性能的奥秘——应用程序中慢,SSMS中快(3)——不总是参数嗅探的错
前面已经提到过关于存储过程在SSMS中运行很快,但在应用程序中运行很慢的可能原因:因为ARITHABORT的不同选项会导致不同的缓存词目,另外由于SQL Server使用了参数嗅探导致获得了不同的执行计划。
虽然已经说明了这个现象的原因,但是还没解释:如何定位和解决这个问题?到目前为止,大家都知道了如何快速处理,如果这个问题很紧急,可以直接使用:
EXEC sp_recompile 存储过程名
前面提到过,这个操作会刷新计划缓存。下次存储过程被调用时,会产生新的查询计划。如果通过这种方式可以解决,那么认为这个已经不是问题了。
但是如果问题依旧,那么就需要做更深入的研究,并且不适合再用sp_recompile或者类似的方式去修改存储过程。另外请一直打开显示执行计划的选项以便用于对比和检查,最起码可以获取参数的值(可以通过右键执行计划→【显示执行计划XML】的方式在XML格式的执行计划中搜索ParameterList的值)。这是本节的主题。
在开始本节之前,给个小建议:修改SSMS的默认值,以便因为ARITHABORT的默认值带来困惑。建议把这个值设为OFF。但是与应用程序相同设置确实会有一些小缺点:你可能观察不到与参数嗅探有关的性能问题。但是如果你已经养成了都校验ARITHABORT ON和OFF的结果,那么这个问题就不成问题了。
获取必要的事实:
Hoping help will come from above
But even angels there make the same mistakes
- 哪个语句慢?
- 不同的查询计划是怎样的?
- SQL Server嗅探了哪些参数?
- 表和索引的定义是怎样的?
- 统计信息的分布情况如何?实时更新吗?
哪个语句慢?
在SSMS中获取查询计划和参数:
SET ARITHABORT ON go EXEC that_very_sp 4711, 123, 1 go SET ARITHABORT OFF go EXEC that_very_sp 4711, 123, 1
从计划缓存中直接获取查询计划和参数:
查询语句:
DECLARE @dbname NVARCHAR(256), @procname NVARCHAR(256) SELECT @dbname = 'Northwind', @procname = 'dbo.List_orders_11'; WITH basedata AS ( SELECT qs.statement_start_offset / 2 AS stmt_start, qs.statement_end_offset / 2 AS stmt_end, est.encrypted AS isencrypted, est.TEXT AS sqltext, epa.value AS set_options, qp.query_plan, charindex('<ParameterList>', qp.query_plan) + len('<ParameterList>') AS paramstart, charindex('</ParameterList>', qp.query_plan) AS paramend FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) est CROSS APPLY sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) qp CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) epa WHERE est.objectid = object_id(@procname) AND est.dbid = db_id(@dbname) AND epa.attribute = 'set_options' ), next_level AS ( SELECT stmt_start, set_options, query_plan, CASE WHEN isencrypted = 1 THEN '-- ENCRYPTED' WHEN stmt_start >= 0 THEN substring(sqltext, stmt_start + 1, CASE stmt_end WHEN 0 THEN datalength(sqltext) ELSE stmt_end - stmt_start + 1 END) END AS Statement, CASE WHEN paramend > paramstart THEN CAST(substring(query_plan, paramstart, paramend - paramstart) AS XML) END AS params FROM basedata ) SELECT set_options AS [SET], n.stmt_start AS Pos, n.Statement, CR.c.value('@Column', 'nvarchar(128)') AS Parameter, CR.c.value('@ParameterCompiledValue', 'nvarchar(128)') AS [Sniffed Value], CAST(query_plan AS XML) AS [Query plan] FROM next_level n CROSS APPLY n.params.nodes('ColumnReference') AS CR(c) ORDER BY n.set_options, n.stmt_start, Parameter
如果再次之前你从来没用过DMVs,估计对你而言会有点难懂。但是为了把注意力集中在我们的主题上。在这里需要提醒的是语句中需要制定数据库和存储过程名。
结果输出:
USE Northwind GO CREATE PROCEDURE List_orders_11 @fromdate DATETIME, @custid NCHAR(5) AS SELECT @fromdate = dateadd(YEAR, 2, @fromdate) SELECT * FROM Orders WHERE OrderDate > @fromdate AND CustomerID = @custid IF @custid = 'ALFKI' CREATE INDEX test ON Orders (ShipVia) SELECT * FROM Orders WHERE CustomerID = @custid AND OrderDate > @fromdate IF @custid = 'ALFKI' DROP INDEX test ON Orders GO SET ARITHABORT ON EXEC List_orders_11 '19980101', 'ALFKI' GO SET ARITHABORT OFF EXEC List_orders_11 '19970101', 'BERGS'
- SET——表示查询计划中set_options属性。正如前面提到的,这是一个位掩码。上图中可以看到两个值:251 为默认设置,4347为默认设置+ARITHABORT ON。如果看到其他值,可以用作者编写的一个函数反编译:setoptions 。
- Pos——表示该语句在存储过程的起始位置,从存储过程创建语句起算,包括任何CREATE PROCEDURE之前的注释。虽然大部分情况下作用不大,但是可以看到语句在存储过程中的出现顺序。
- Statement——SQL语句,但是注意对于每个参数,都会重复一次。
- Parameter——参数名,仅列出语句中出现的参数。也就是说,如果该查询语句没有使用到的参数,是不会出现在这里的。
- Sniffed Value——在编译时的参数值,也就是在嗅探优化并产生查询计划的参数值。因为这是从计划缓存中获取的信息,所以这里的值并不是实际参数值,从上图中可以看到,对于不同的语句可能会出现不同的参数值。
- Query Plan——对应的预估执行计划。
从Trace文件中获取查询计划和参数:
获取表和索引的定义:
DECLARE @tbl NVARCHAR(265) SELECT @tbl = 'Orders' SELECT o.NAME, i.index_id, i.NAME, i.type_desc, substring(ikey.cols, 3, len(ikey.cols)) AS key_cols, substring(inc.cols, 3, len(inc.cols)) AS included_cols, stats_date(o.object_id, i.index_id) AS stats_date, i.filter_definition FROM sys.objects o JOIN sys.indexes i ON i.object_id = o.object_id CROSS APPLY ( SELECT ', ' + c.NAME + CASE ic.is_descending_key WHEN 1 THEN ' DESC' ELSE '' END FROM sys.index_columns ic JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0 ORDER BY ic.key_ordinal FOR XML PATH('') ) AS ikey(cols) OUTER APPLY ( SELECT ', ' + c.NAME FROM sys.index_columns ic JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1 ORDER BY ic.index_column_id FOR XML PATH('') ) AS inc(cols) WHERE o.NAME = @tbl AND i.type IN ( 1, 2 ) ORDER BY o.NAME, i.index_id
这个语句仅用于常规关系型索引,不适合XML索引和空间索引。
获取统计信息相关信息:
DECLARE @tbl NVARCHAR(265) SELECT @tbl = 'Orders' SELECT o.NAME, s.stats_id, s.NAME, s.auto_created, s.user_created, substring(scols.cols, 3, len(scols.cols)) AS stat_cols, stats_date(o.object_id, s.stats_id) AS stats_date, s.filter_definition FROM sys.objects o JOIN sys.stats s ON s.object_id = o.object_id CROSS APPLY ( SELECT ', ' + c.NAME FROM sys.stats_columns sc JOIN sys.columns c ON sc.object_id = c.object_id AND sc.column_id = c.column_id WHERE sc.object_id = s.object_id AND sc.stats_id = s.stats_id ORDER BY sc.stats_column_id FOR XML PATH('') ) AS scols(cols) WHERE o.NAME = @tbl ORDER BY o.NAME, s.stats_id
其中列stats_date返回统计信息最近更新时间。如果这个时间已经过去很久,那么统计信息可能已经过时。参数嗅探问题的根源通常不是统计信息过时,但是也应该检查一下。需要记住的是,统计信息的列如果是单调递增——如ID、date列,那么会很快过时,因为语句通常获取最近插入的数据,在统计信息的直方图中,记录的却通常是旧的数据。关于直方图会在后续介绍。
UPDATE STATISTICS 表名 WITH FULLSCAN, INDEX
UPDATE STATISTICS tbl indexname WITH FULLSCAN
注意:在表名和索引名之间没有句号,只有空格。
如果是统计信息过时导致的性能问题,通常在更新统计信息之后,就可以发现应用程序的性能马上就得到提升。因为统计信息的更新会触发重编译,使得存储过程会对参数重新嗅探并产生更好的执行计划。
DBCC SHOW_STATISTICS (Orders, OrderDate)
小结:
理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息的更多相关文章
-
理解性能的奥秘——应用程序中慢,SSMS中快(4)收集解决参数嗅探问题的信息
---从计划缓存中直接获取查询计划和参数: ), ) SELECT @dbname = 'hydee_连锁', @procname = 'dbo.p_select_ware'; WITH baseda ...
-
理解性能的奥秘——应用程序中慢,SSMS中快(5)——案例:如何应对参数嗅探
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(4)--收集解决参数嗅探问题的信息 首先我们需要明白,参数嗅探本身不是问 ...
-
理解性能的奥秘——应用程序中慢,SSMS中快(3)——不总是参数嗅探的错
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(2)--SQL Server如何编译存储过程 在我们开始深入研究如何处理 ...
-
理解性能的奥秘——应用程序中慢,SSMS中快(1)——简介
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 在工作中发现有不少类似的现象,有幸看到国外大牛写的一篇文章,由于已经完善得不能再添油加醋,所以决定直接翻译,原文出处:http ...
-
理解性能的奥秘——应用程序中慢,SSMS中快(6)——SQL Server如何编译动态SQL
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(5)--案例:如何应对参数嗅探 我们抛开参数嗅探的话题,回到了本系列的最 ...
-
理解性能的奥秘——应用程序中慢,SSMS中快(2)——SQL Server如何编译存储过程
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(1)--简介 本文介绍SQL Server如何编译存储过程并使用计划缓存 ...
-
[转]提高 Linux 上 socket 性能,加速网络应用程序的 4 种方法
原文链接:http://www.ibm.com/developerworks/cn/linux/l-hisock.html 使用 Sockets API,我们可以开发客户机和服务器应用程序,它们可以在 ...
-
1 开发一个注重性能的JDBC应用程序不是一件容易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告诉你。 本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的原则已经被许多现有的JDBC应用程序编译运行并验证过。 这些指导原则包括: 正确的使用数据库MetaData方法 只获取需要的数据 选用最佳性能的功能 管理连
1 开发一个注重性能的JDBC应用程序不是一件容易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告诉你. 本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的 ...
-
【SQL server初级】数据库性能优化三:程序操作优化
数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分 数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少 ...
随机推荐
-
NuGet镜像上线试运行
为解决国内访问NuGet服务器速度不稳定的问题,我们用阿里云服务器搭建了一个NuGet镜像,目前已上线试运行. 使用NuGet镜像源的方法如下: 1)NuGet镜像源地址:https://nuget. ...
-
Linux网络编程系列-TCP编程实例
实例: client #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #inc ...
-
paip.输入法编程---词频顺序order by py
paip.输入法编程---词频顺序order by py 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn ...
-
<;meta http-equiv=";pragma"; content=";no-cache";/>;-备
<meta http-equiv="pragma" content="no-cache"/> 网页中常常看见有这样的标记,他们是清浏览器缓存用的啊, ...
-
解决表格里面使用text-overflow后依旧不能隐藏超出的文本
解决表格里面使用text-overflow后依旧不能隐藏超出的文本 来源: http://blog.csdn.net/colinmuxi/article/details/9069595 (非原创,自 ...
-
mysql并发控制之快照读和当前读
上一篇简单的介绍了下MVCC(多版本并发控制)的原理,MVCC会对事物内操作的数据做多版本控制,从而实现并发环境下事物对数据写操作的阻塞不影响读操作的性能.而这个多版本控制的实现是由undo log来 ...
-
Elasticsearch&#160;Query&#160;DSL
Elasticsearch Query DSL By:授客 QQ:1033553122 1. match_all 1 2. match 2 3. match_phrase 5 4. match_phr ...
-
settings.xml配置详解
简单值 一半顶层settings元素是简单值,它们表示的一系列值可以配置Maven的核心行为:settings.xml中的简单顶层元素 < settings xmlns="http:/ ...
-
IDEA中配置Maven+spring MVC+tomcat
一:配置Maven安装教程如下: http://blog.csdn.net/qq_32588349/article/details/51461182 实际安装过程中,如果按照教程配置如下属性,最后创建 ...
-
《杜增强讲Unity之Tanks坦克大战》7-坦克血条
7 坦克血条 点击菜单GameObject->UI->Slider创建Slider 选中EventSystem,设置Horizontal Axis为HorzontalUI,Vertic ...