SQL Server 2008 R2上的SQL Server语句间歇性变慢


13

对于我们的一位客户,我们的应用程序遇到了一些性能问题。这是一个.NET 3.5 Web应用程序,正在使用和更新SQL Server数据库上的数据。当前,我们的生产环境由一台Windows 2008 R2计算机作为前端,而在后端则是一个SQL Server 2008 R2群集。我们的应用程序使用COM +和MSDTC连接到数据库。

这是正在发生的事情:我们的最终用户有时会抱怨应用程序运行缓慢。某些页面的加载时间比预期的要多。在尝试找出正在发生的事情时,我设法找出了数据库方面的一些奇怪行为,这可能是性能下降的原因。我注意到有时有些SQL语句需要花费更多的时间才能运行。我设法使用探查器跟踪(带有TSQL_Duration模板)来识别长时间运行的查询,从而识别其中一些语句(主要是对应用程序存储过程的调用)。

问题是,当我直接在SQL Management Studio的数据库上运行这些存储过程时,有时它们会花费很长时间(大约7/8秒),而其他时候它们却很快(不到1秒)。我不知道为什么会这样,这让我发疯,因为其他任何应用程序都没有使用SQL机器(4核,32 GB),并且这些查询的运行时间不会太长。

不是DBA或SQL Server专家,我一直在尝试研究一些可以帮助我理解问题的东西。这是我尝试解决问题以及到目前为止发现的步骤:

  • 应用程序调用的所有TSQL代码都是在存储过程中编写的。
  • 我在SQL Server事件探查器上确定了一些长时间运行的查询,但是,当我在Management Studio上运行这些查询时,它们要么运行很长时间(从4到10秒),要么运行很快(不到1秒)。我正在使用参数中传递的相同数据运行完全相同的查询。这些查询主要是存储过程,其中包含选择语句。
  • 我尝试查看等待和排队统计信息,以尝试确定是否有某些资源在等待进程。我运行了以下查询:

WITH Waits AS
    (SELECT
        wait_type,
        wait_time_ms / 1000.0 AS WaitS,
        (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
        signal_wait_time_ms / 1000.0 AS SignalS,
        waiting_tasks_count AS WaitCount,
        100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
        ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
        'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',  'BROKER_TO_FLUSH',
        'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',     'DISPATCHER_QUEUE_SEMAPHORE',
        'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
        'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
        'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
        'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
    )
SELECT
    W1.wait_type AS WaitType, 
    CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
    CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
    CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
    W1.WaitCount AS WaitCount,
    CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
    CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
    CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
    CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
    INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount,    W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO

这是我发现的:

  • 在我使用DBCC SQLPERF重置统计信息后(大约1或2小时后),我最等待的等待类型是SOS_SCHEDULER_YIELD和WRITELOG
  • 随着时间的推移(执行大约1天后),即使每种数据库的平均等待时间不长,在数据库上发生最多的等待类型还是CXPACKET(67%)和OLEDB(17%)。我还注意到,SQL Profiler上标识的运行时间更长的语句是对存储过程的调用,这些存储过程返回多个结果集(通常为3个)。这里会出现并列问题吗?有什么方法可以尝试确定这是否是问题的原因?
  • 我读过某个地方,OLEDB等待可能是由对链接服务器等OLEDB资源的调用引起的。我们确实有一个链接服务器与Indexing Services计算机(MSIDXS)连接,但是没有一个被确定为长时间运行的语句都使用该链接服务器。
  • 对于LCK_M_X类型的等待,我有更高的平均等待时间(平均约1.5秒),但是与其他类型的等待相比,这些等待类型却很少发生(例如,64 LCK_M_X等待与10,823 CXPACKET在同一时间等待)。
  • 我注意到的一件事是MSDTC服务不是群集的。SQL Server服务是群集的,但不是MSDTC的。是否可以因此而对性能产生影响?我们之所以使用MSDTC,是因为我们的应用程序使用企业服务(DCOM)来访问数据库,但是服务器不是由我们而是由客户端安装和配置的。

谁能帮助我进一步了解这些数据?谁能帮助我了解可能发生的事情?我可以在服务器上做一些事情来尝试解决问题吗?我应该与应用程序开发团队联系吗?

Answers:


4

感谢您对问题的详细解释(实际上是提出的最好的问题之一)。

WRITELOG是一种非常常见的等待类型,因此不必担心。查看SOS_SCHEDULER_YIELD既表明CPU压力,也表明CXPACKET,则可能必须缺少一些索引,并且您可能正在从OLTP系统的查询中检索大量数据。我建议您查看Missing Indexes DMV,看看有问题的过程中是否有任何索引(几乎肯定会有更多)。

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the-创建索引命令/

也可以在sqlblog.com上查找Jonathan Kehayias的帖子。

另外,看看参数嗅探。

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

这不是满足您需求的竞争解决方案,而是一个很好的起点。让我们知道您是否需要更多详细信息。


1

在一名员工重写了一些存储过程之后,我们遇到了类似的问题。事实证明,存在过多的分支,并且正在构建的Dynamic SQL大大改变了where子句。

例如(当然是简化的):

如果模型为“ X”,那么where子句查找的ProductCode等于某些值。
如果Model为“ Y”,那么where子句查找的ProductType等于某些值。

第一次执行存储过程时,SQL Server将基于输入参数构建查询计划。因此,如果查询计划是基于使用“ ProductCode”等于“”的逻辑构建的,而您要求的“ ProductType”等于“逻辑”,则这是不匹配的查询计划,并且很可能导致全表扫描。

您可以尝试将“ WITH RECOMPILE ”放在存储过程的顶部。 创建过程(Transact-SQL)

我能描述的最好方法如下:

假设您具有按姓氏排序的姓名和电话号码列表。这对于寻找使用其姓氏的人(基于姓氏的查询计划)非常有用。现在,假设您需要区号203中的所有姓名和电话号码。如果您的列表按姓氏排序,则要获取所有区号203人的完整列表的唯一方法是从顶部开始,依次逐个阅读每条记录。(全表扫描)。


使用exec()功能可以解释观察到的行为。在这种情况下,sp_executesql通常可以使用动态SQL语句解决问题。
2013年

1

如果查询在SSMS和应用程序中间歇地快速运行,则可能会出现统计信息或参数嗅探问题。

我将执行这些存储过程,然后查看执行计划以拉起根运算符的属性(每个语句最左侧的绿色节点)。

执行计划中估计的行数是多少,而实际返回的行数是多少?

编译后的参数是否与实际查询参数匹配?

如果为仅返回少量行的参数创建了执行计划,并且使用返回大量行的参数运行了相同的过程,则SQL可能会对查询使用错误的执行计划。

执行计划的选择与SQL统计信息紧密相关,因此最好定期重建统计信息。

如果您的存储过程有时会根据提供的参数返回少量数据或大量数据,则可能存在参数嗅探问题。

如果重建统计信息不能解决问题,则可以使用以下命令在存储过程中运行最昂贵的语句: OPTION (RECOMPILE)


0

确定了长时间运行的查询后,可以从缓存中获取这些过程的执行计划,并查看是否可以在其中确定问题。通常,存在数据类型的隐式或运行时转换。另外,如果您清除或插入大量数据,建议也更新统计信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.