【问题标题】:Corrupt Azure SQL stored procedure could only be fixed by drop recreate损坏的 Azure SQL 存储过程只能通过 drop recreate 来修复
【发布时间】:2016-01-07 15:40:22
【问题描述】:

如果这是重复的,请原谅我。我能找到的最接近的是Random timeout running a stored proc - drop recreate fixes,但我不确定那里关于重新编译存储过程的答案是否适用。

我有一个最新版本的 Azure SQL 数据库,它有大量来自 Azure Web 应用程序前端的流量。我有一个夜间远程作业,它运行批处理以在 Azure SQL 数据库上重建索引,因为这似乎对控制数据库大小和性能有很大帮助。

通常,重建索引大约需要 20 分钟。昨晚2小时后超时。该批次中的错误处理程序没有记录任何错误。

重建索引开始后不久,一个特定的存储过程开始对每个调用它的客户端超时。使用相同表的其他存储过程没有任何问题。当我发现问题时,我可以通过将存储过程更改为立即返回来缓解所有超时和挂起的进程。当我再次更改存储过程以使其正常运行时,问题立即再次出现。我的理解是更改存储过程会迫使它重新编译,但这并没有解决它。

最终,我完全放弃并使用原始代码重新创建了该过程,问题得到了解决。

这个过程和它使用的架构已经完全稳定了好几个月。程序本身非常简单:

CREATE Procedure [dbo].[uspActivityGet] (@databaseid uniqueidentifier) AS
begin
    SET NOCOUNT ON;
    --There may be writing activities to the table asynchronously, do not use nolock on tblActivity - the ActivityBlob might be null in a dirty read.
    select top 100 a.Id, h.HandsetNumber, a.ActivityBlob, a.ActivityReceived
    from dbo.tblDatabases d with(nolock) join dbo.tblHandsets h with(nolock) on d.DatabaseId = h.DatabaseId join dbo.tblActivity a on h.Id = a.HandsetId
    where d.DatabaseId = @databaseid and a.ActivitySent is null
    order by a.ActivityReceived
end

虽然程序会挂起并出现类似这样的超时:

exec dbo.uspActivityGet 'AF3EA01B-DB22-4A39-9E1C-D096D2DF1215'

在查询窗口中运行相同的选择会迅速成功地返回:

declare @databaseid uniqueidentifier; set @databaseid = 'AF3EA01B-DB22-4A39-9E1C-D096D2DF1215'
select top 100 a.Id, h.HandsetNumber, a.ActivityBlob, a.ActivityReceived
from dbo.tblDatabases d with(nolock) join dbo.tblHandsets h with(nolock) on d.DatabaseId = h.DatabaseId join dbo.tblActivity a on h.Id = a.HandsetId
where d.DatabaseId = @databaseid and a.ActivitySent is null
order by a.ActivityReceived

有什么想法可以防止这种情况在未来发生吗?谢谢。

编辑 - 添加执行计划截图

编辑 - 添加用于查看正在运行的进程的查询。有很多,猜测大约有 150 个,处于挂起状态,它们都用于同一个存储过程 - uspActivityGet。此外,Data IO Percentage 在高峰需求时间通常运行 20 - 40% 时一直处于最大值。我不记得等待类型是什么。这是用于查看的查询。

select * from sys.dm_Exec_requests r with(nolock) CROSS APPLY sys.dm_exec_sql_text(r.sql_handle)  order by r.total_elapsed_time desc

编辑 - 今晚又发生了。这是问题期间相同过程的执行计划。再次删除并创建过程后,执行计划恢复正常,问题得到解决。

在问题期间,执行相同查询的 sp_executesql 大约需要 5 分钟,我相信这代表了正在发生的事情。大约有 50 个 uspActivityGet 实例因等待类型 SLEEP_TASK 或 IO_QUEUE_LIMIT 而暂停。

也许下一个问题是为什么索引重建或其他夜间维护会对执行计划执行此操作?

【问题讨论】:

  • 其他没有问题的程序使用相同的连接?也许不同的程序利用不同的索引? d.DatabaseId 是否已编入索引?是否需要NOLOCK 提示?也许索引问题被那个提示隐藏了?如果在过程失败时从 SSMS 运行查询会发生什么?
  • @Paolo 在查询窗口中运行相同的 select 语句将立即成功返回。
  • 您是否能够判断 proc 是否被阻止或长时间执行某项操作或其他原因?当您在查询中将 GUID 作为常量提供时,它会更改 SQL Server 查询成本的方式,因此测试并不会真正以相同的方式运行两个查询。尝试使用 sp_executesql N'--your query', N'@databaseid uniqueidentifier', '--your param' 进行测试,看看它是否仍然快速运行或陷入困境。此外,请分享您的执行计划,以便我们了解 SQL Server 的想法
  • @SQLmojoe 由于我删除并重新创建了该过程,因此无论我如何测试它现在都表现良好。我已经添加了当前的执行计划以及在问题期间观察到的关于暂停进程的内容。如果我看到它再次发生,我应该寻找一些更好的细节吗?谢谢。
  • 您是否尝试过 Paolo 的建议并从查询中删除了 NOLOCK 提示?另外,也许您也可以在重建索引之前尝试更新统计信息?另一个尝试:在存储过程中添加WITH RECOMPILE?也许某些东西会改变您所看到的行为并引导您找到更强大的解决方案?总帐。

标签: sql-server stored-procedures azure-sql-database


【解决方案1】:

线索在查询和麻烦的执行计划中。见Poor Performance with Parallelism and Top

正常的执行计划似乎非常有效,只要相关架构没有改变,就不需要重新编译。我还想避免此查询中的并行性。我在查询中添加了以下两个选项,以确保这两点都得到保证,一切都再次得到满足。

OPTION (KEEPFIXED PLAN, MAXDOP 1)

【讨论】:

  • 很高兴看到你解决了这个问题 =)
猜你喜欢
  • 2011-01-30
  • 2011-04-01
  • 2012-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 2013-09-11
  • 1970-01-01
相关资源
最近更新 更多