MS SQL Server 2005 - 存储过程“自发中断”

Posted

技术标签:

【中文标题】MS SQL Server 2005 - 存储过程“自发中断”【英文标题】:MS SQL Server 2005 - Stored Procedure "Spontaneously Breaks" 【发布时间】:2009-06-18 21:19:55 【问题描述】:

客户端报告在执行存储过程时重复出现非常奇怪的行为。

他们的代码运行在一个易失数据集的缓存转置上。如果满足以下条件,则会编写存储过程以按需重新处理数据集: 1. 自上次重新处理后数据集发生了变化 2. 数据集5分钟没有变化

(第二个条件在变化期间停止大量重复重新计算。)

这在几周内运行良好,SP 需要 1-2 秒才能完成重新处理,而且它只在需要时才这样做。那么……

SP 突然“停止工作”(它只是继续运行并且从未返回) 我们以一种微妙的方式改变了 SP,它再次起作用了 几天后它再次停止工作 然后有人说“我们以前见过,只需重新编译 SP” 在代码不变的情况下,我们重新编译了 SP,它工作了 几天后它再次停止工作

这已经重复了很多很多次了。 SP 突然“停止工作”,不再返回,客户端超时。 (我们尝试通过 management studio 运行,15 分钟后取消查询。)

然而每次我们重新编译 SP 时,它突然又可以工作了。

我还没有在适当的 EXEC 语句上尝试 WITH RECOMPILE,但我并不想以任何方式这样做。它每小时被调用数百次,通常什么都不做(它每天只重新处理数据几次)。如果可能的话,我想避免重新编译相对复杂的 SP 的开销“只是为了避免“不应该”发生的事情......

以前有没有人经历过这种情况? 有人对如何克服它有任何建议吗?

干杯, 德姆斯。

编辑:

伪代码如下:

从 table_x 中读取“a” 从 table_x 中读取“b” 如果 (a 开始交易 删除表_y INSERT INTO table_y 更新表_x 提交交易

选择“不漂亮”,但是当在线执行时,它们会立即执行。包括当 SP 拒绝完成时。探查器显示它是 SP“停止”的 INSERT

SP 没有参数,sp_lock 显示没有阻塞进程。

【问题讨论】:

听起来好像您有一个未提交或回滚的事务。不看代码很难说。 哦,下载最新的服务包和更新永远不会有什么坏处。 它必须是一个LOCK,或者至少表现得像这样...... 我们的客户已将所有 IT 外包给 IBM。他们只在自己喜欢的时候修补什么。 “在重新运行 ALTER 语句后立即运行完美”这太巧合了,而且 sp_lock 没有显示任何相关信息。 (嗯,sp_lock3 是从某人的网站上复制的) 【参考方案1】:

这是参数嗅探的足迹。是的,第一步是尝试 RECOMPILE,尽管它在 2005 年并不总是按照您希望的方式工作。

更新: 无论如何,我都会在 INSERT 上尝试语句级重新编译,因为这可能是一个统计问题(哦,是的,检查自动统计更新是否已打开)。

如果这似乎不适合参数嗅探,则比较实际的查询计划,从它正常工作的时候开始,从它永远运行的时候开始(如果你不能得到实际的,使用估计的计划,虽然实际更好)。您正在查看计划是否更改。

【讨论】:

很遗憾,没有参数。唯一的变量是正在处理的源数据集的内容。【参考方案2】:

我完全同意参数嗅探诊断。如果您的 SP 输入参数是变化的(或者即使它们没有变化) - 请务必使用局部变量屏蔽它们并在 SP 中使用局部变量。

如果集合正在更改但查询计划不再有效,您也可以使用WITH RECOMPILE

在 SQL Server 2008 中,您可以使用OPTIMIZE FOR UNKNOWN 功能。

另外,如果您的流程涉及填充一个表,然后在另一个操作中使用该表,我建议将该流程分解为单独的 SP 并单独调用它们 WITH RECOMPILE。我认为,当您填充表然后使用该表的结果执行操作时,在流程开始时生成的计划有时可能非常差(差到无法完成)。因为在最初计划时,表与最初插入后有很大不同。

【讨论】:

没有参数,数据集中的变化是微妙的,不重要的。执行计划根本不需要改变。这使它如此混乱。就好像执行计划在不应该改变的时候改变了,就像损坏的统计数据一样。但我们也检查了这些! 叹息 我添加了一些关于使用多个表来获取中间结果的长流程的注释。 在这种情况下没有中间步骤。只是一个 DELETE 然后一个 INSERT,伪代码中提到的 UPDATE 只是将 GetDate() 输入到元数据控制表中。【参考方案3】:

正如其他人所说,数据或源表统计信息的变化方式导致缓存的查询计划过时。

WITH RECOMPILE 可能是最快的解决方法 - 使用 SET STATISTICS TIME ON 找出重新编译的实际成本是多少,然后再取消它。

如果这仍然不是一个可接受的解决方案,最好的选择可能是尝试重构插入语句。

您没有在插入语句中说明您使用的是UNION 还是UNION ALL。我见过INSERT INTOUNION 产生了一些奇怪的查询计划,尤其是在SQL 2005 的SP2 之前的版本上。

Raj 建议放弃和 重新创建目标表 SELECT INTO 是一种方法。

您也可以尝试选择每个 三个源查询变成自己的 临时表,然后UNION 那些临时表 一起插入。

或者,您可以尝试 这些建议的结合—— 将联合的结果放入 带有SELECT INTO 的临时表, 然后从中插入目标 表。

我已经看到所有这些方法都可以解决类似情况下的性能问题;测试将揭示哪种方法能够根据您拥有的数据提供最佳结果。

【讨论】:

【参考方案4】:

显然更改存储过程(通过重新编译)会改变导致锁定的情况。

尝试按照here 或here 的说明记录您的 SP 的进度。

【讨论】:

【参考方案5】:

我同意上面在评论中给出的答案,这听起来像是一个未关闭的事务,特别是如果您仍然能够从查询分析器运行 select 语句。

听起来很像有一个打开的事务,其中 table_y 的待删除,此时插入不能发生。

当您的 SP 锁定时,您可以对 table_y 执行插入操作吗?

【讨论】:

唯一写入该表的代码是有问题的 SP。由于代码是 BEGIN TRANSACTION, DELETE , INSERT INTO
, UPDATE , COMMIT TRANSACTION 我看不出所描述的场景可能出现在哪里。特别是因为 sp_lock 没有显示任何锁争用。 只是出于兴趣 - 如果此过程运行两次会发生什么 - 而第一个过程仍在执行?
【参考方案6】:

您有索引维护工作吗?

您的统计数据是最新的吗?一种判断方法是检查估计的和实际的查询计划是否有较大的变化。

【讨论】:

IBM 拥有所有客户 SQL SERVER 实例的 DBA 卷。索引在夜间过程中维护。我必须在工作时检查实际计划与估计计划,然后在“损坏”时检查估计计划。当它损坏时,我无法获得 ACTUAL,因为它似乎永远不会返回。而且,不,我们不能让一个锁定的表等待它在实时系统上重新运行几个小时:)【参考方案7】:

正如其他人所说,这听起来很可能是一个未提交的事务。

我的最佳猜测:

您需要确保 table_y 可以完全且快速地删除。

如果有其他存储过程或外部代码曾经在此表上保存事务,您可能会永远等待。 (他们可能会出错并且永远不会关闭交易)

另一个注意事项:如果可能,请尝试使用 truncate。它比没有 where 子句的删除使用更少的资源:

truncate table table_y

此外,一旦您的 OWN 事务中发生错误,它将导致所有后续调用(显然每 5 分钟一次)“挂起”,除非您处理错误:

begin tran
begin try
 -- do normal stuff
end try
begin catch
 rollback
end catch
commit

第一个错误将为您提供有关实际错误的信息。在您自己的后续测试中看到它挂起只是次要效果。

【讨论】:

【参考方案8】:

如果您正在执行以下步骤:

DELETE table_y
INSERT INTO table_y <3 selects unioned together>

你可能想试试这个

DROP TABLE table_y
SELECT INTO table_y <3 selects unioned together>

【讨论】:

我认为您的意思是 TRUNCATE TABLE 而不是 DROP TABLE。此外,调用存储过程的安全上下文无法 TRUNCATE 表,只有 DELETE 有效。此外,问题在于 INSERT,而不是清除数据。 但是数据在批处理中发生变化的事实意味着在批处理开始时选择的执行计划可能很差,因此我的观点是分段获得更好的执行计划。

以上是关于MS SQL Server 2005 - 存储过程“自发中断”的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005 中没有过程的查询计划

如何生成将使用数据重建我的 MS SQL Server 2005 数据库的脚本?

存储过程系列之调试存储过程 SQL Server 2005

使用存储过程将记录从 SQL Server 复制到 SQL Server (2005)

存储过程系列之调试存储过程 SQL Server 2005

在存储过程 sql server 2005 中使用函数调用?