【问题标题】:Insert query times out in C# web app, runs fine from SQL Server Management Studio在 C# Web 应用程序中插入查询超时,在 SQL Server Management Studio 中运行良好
【发布时间】:2013-01-03 19:14:16
【问题描述】:

我正在尝试从我的 C# Web 应用程序中运行插入查询。当我从 SQL Server Management Studio 运行查询时,插入查询大约需要五分钟才能完成。从应用程序运行时,它会在三十分钟后超时(是分钟,而不是秒)。

我已经从 VS 调试器中获取了实际的 SQL 语句,并从 Mgmt Studio 中运行它,它运行良好。

所有这些都是在我的开发环境中运行的,而不是生产环境。查询正在进行时没有其他 SQL Server 活动。我正在使用 SQL Server 2008 R2 进行开发。 MS VS 2010 Express,Asp.Net 4.0。 SQL Server Mgmt Studio 10。

有一个类似的问题从未得到回答:SQL server timeout 2000 from C# .NET

这是来自 dbcc useroptions 的 SET 选项

Option                  MgtStudio      Application
----------------------- -------------- --------------
textsize                2147483647     -1
language                us_english     us_english
dateformat              mdy            mdy
datefirst               7              7
lock_timeout            -1             -1
quoted_identifier       SET            SET
arithabort              SET            NOT SET
ansi_null_dflt_on       SET            SET
ansi_warnings           SET            SET
ansi_padding            SET            SET
ansi_nulls              SET            SET
concat_null_yields_null SET            SET
isolation level         read committed read committed

只有 textsize 和 arithabort 不同。

任何想法为什么查询执行时间存在如此差异以及我可以做些什么来缩小这种差异?

我不确定包含查询会有多大用处,尤其是因为包含架构太多了。无论如何,这里是:

INSERT INTO GeocacherPoints
            (CacherID,
             RegionID,
             Board,
             Control,
             Points)
SELECT z.CacherID,
       z.RegionID,
       z.Board,
       21,
       z.Points
FROM   (SELECT CacherID,
               gp.RegionID,
               Board=gp.Board + 10,
               ( CASE
                   WHEN (SELECT COUNT(*)
                         FROM   Geocache g
                                JOIN GeocacheRegions r
                                  ON ( r.CacheID = g.ID )
                         WHERE  r.RegionID = gp.RegionID
                                AND g.FinderPoints >= 5) < 20 THEN NULL
                   ELSE (SELECT SUM(y.FinderPoints) / 20
                         FROM   (SELECT x.FinderPoints,
                                        ROW_NUMBER() OVER (ORDER BY x.FinderPoints DESC, x.ID) AS Row
                                 FROM   (SELECT g.FinderPoints,
                                                g.ID
                                         FROM   Geocache g
                                                JOIN Log l
                                                  ON ( l.CacheID = g.ID )
                                                JOIN Geocacher c
                                                  ON ( c.ID = l.CacherID )
                                                JOIN GeocacheRegions r
                                                  ON ( r.CacheID = g.ID )
                                         WHERE  YEAR(l.LogDate) = @Year
                                                AND g.FinderPoints >= 5
                                                AND c.ID = gp.CacherID
                                                AND r.RegionID = gp.RegionID) x) y
                         WHERE  y.Row <= 20)
                 END ) Points
        FROM   GeocacherPoints gp
               JOIN Region r
                 ON r.RegionID = gp.RegionID
        WHERE  gp.Control = 21
               AND r.RegionType IN ( 'All', 'State' )
               AND gp.Board = @Board - 10) z
WHERE  z.Points IS NOT NULL
       AND z.Points >= 1 

【问题讨论】:

  • 我刚刚回答,但我意识到你说它在 30 分钟后超时,而不是几秒;这是正确的数字还是错字(即它在 30 秒后超时,而不是几分钟)?
  • 在插入记录时尝试指定表中声明的参数的确切类型。
  • 由于花费的时间超过理想时间,请考虑指定 commandtimeout = 0 以允许查询无限期执行。
  • @Sundeep 我没有听从你的建议,这怎么会导致应用程序中的查询时间比 Management Studio 中的时间长 6 倍以上?
  • 查询在做什么?您是在尝试加载数据集、读取器还是只是执行非查询?

标签: c# asp.net sql-server


【解决方案1】:

ARITHABORT 经常被误诊为原因。

事实上,自 2005 版以来,ANSI_WARNINGS 开启(因为它在您的两个连接中)ARITHABORT 无论如何都隐式开启,此设置没有实际效果。

但它确实有副作用。为了考虑到ANSI_WARNINGS 关闭的情况,ARITHABORT 的设置被用作plan cache keys 之一,这意味着具有不同设置的会话不能共享彼此的计划。

当您在 SSMS 中运行查询时,无法重用为您的应用程序缓存的执行计划,除非它们都具有相同的计划缓存键,因此它会编译一个新计划,以“嗅探”当前正在测试的参数值。您的应用程序的计划可能是针对不同的参数值编译的。这个问题被称为“参数嗅探”。

您可以检索两个执行计划并将其与类似的东西进行比较

SELECT usecounts, cacheobjtype, objtype, text, query_plan, value as set_options
FROM sys.dm_exec_cached_plans 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
cross APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa
where text like '%INSERT INTO GeocacherPoints (CacherID,RegionID,Board,Control,Points)%' 
and attribute='set_options' and text not like '%this query%'

XML 中的参数部分告诉您参数的编译时间值。

请参阅Slow in the Application, Fast in SSMS? Understanding Performance Mysteries 了解更多信息。

执行计划

您提供了估计的执行计划而不是实际的执行计划,但可以看出只有第一个查询计划是参数化的,并且它是针对以下值编译的。

        <ParameterList>
          <ColumnReference Column="@Dec31" ParameterCompiledValue="'2013-12-31'" />
          <ColumnReference Column="@Jan1" ParameterCompiledValue="'2013-01-01'" />
          <ColumnReference Column="@Board" ParameterCompiledValue="(71)" />
        </ParameterList>

第二个执行计划使用变量而不是参数。这极大地改变了事情。

DECLARE @Board INT
DECLARE @Jan1 DATE
DECLARE @Dec31 DATE

SET @Board=71
SET @Jan1='January 1, 2013'
SET @Dec31='December 31, 2013'

INSERT INTO GeocacherPoints

SQL Server 不会嗅探变量的特定值并生成类似于使用OPTIMIZE FOR UNKNOWN 提示的通用计划。该计划中的估计行数远高于第一个计划。

您没有说明哪个是快计划,哪个是慢计划。如果使用变量的速度更快,那么您可能需要更新统计信息,您很可能会遇到此处描述的问题Statistics, row estimations and the ascending date column 如果使用参数的速度更快,那么您将能够实现变量嗅探并考虑到使用OPTION (RECOMPILE) 提示的实际变量值。

【讨论】:

  • 那是一篇有趣的文章。执行时间的差异对我来说开始有些意义,但是我仍然需要了解要克服哪些变化。我的第一个想法是更改 WHERE YEAR(l.LogDate)=@Year 以便 l.LogDate 上的索引可以生效。
  • 所以现在的问题是如何使执行计划更优化。我在 MS SQL Server Mgmt Studio 中关闭了 ARITHABORT,它仍然在大约相同的时间内运行。
  • 我进行了两项更改,现在它在 Mgmt Studio 中运行时间不到 30 秒。首先,我将 WHERE YEAR(l.LogDate)=\@Year 更改为 WHERE \@Jan1
  • @russ - 在 SSMS 中关闭 ARITHABORT 并不意味着您最终使用与 @987654336 以外的其他计划缓存键相同的计划@。要查看他们更改我的问题中的查询以从 WHERE 中删除 attribute='set_options' 并将 attribute,value,is_cache_key 添加到 SELECT 列表中。您能否发布您(a)从 SSMS(b)从您的应用程序中获得的执行计划?
  • 抱歉耽搁了,但工作会妨碍您。我不知道如何发布执行计划,否则我会的。感谢您的帮助。
【解决方案2】:

如果您使用 SqlCommand 并且没有为 CommandTimeout 指定值,它将在 30 秒后自动超时。

【讨论】:

  • OP 的意思是 30 秒吗……因为它说的是 30 分钟
  • @MichaelPerrenoud:我在重新阅读后才意识到这一点并问了同样的问题。
  • 太好了,30 秒对我来说也更有意义!
  • 我的意思是 30 分钟,而不是几秒钟。
【解决方案3】:

这个问题似乎会定期出现,此链接位于列表顶部: Query times out from web app but runs fine from management studioARITHABORT 显然是罪魁祸首。

【讨论】:

  • ARITHABORT 不是原因。当ansi_warnings 处于打开状态时,它没有任何功能影响,除了副作用之外,它被用作计划缓存键之一,因此阻止了执行计划的共享。不同的缓存计划是实际问题。
猜你喜欢
  • 2016-05-02
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 1970-01-01
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
相关资源
最近更新 更多