sql server 2005 死锁在生产中超时,而不是在测试环境中:为啥?

Posted

技术标签:

【中文标题】sql server 2005 死锁在生产中超时,而不是在测试环境中:为啥?【英文标题】:sql server 2005 deadlock times out in production, not in test environment: why?sql server 2005 死锁在生产中超时,而不是在测试环境中:为什么? 【发布时间】:2009-08-27 15:05:05 【问题描述】:

在我的开发环境中,我试图重新创建一个生产问题,我们 面对 MSSQL 2005。这个问题有两个部分:

问题

1) 发生死锁,MSSQL 选择一个连接(“连接 X”)作为“受害者”。 2) 所有后续使用“连接 X”的尝试都失败了(我们使用连接池)。 MSSQL 提示“服务器无法恢复事务”

在这两者中,#2 如果更严重:因为“连接 X”每 “循环”尝试重新使用“连接 x”失败——而且很神秘 “随机”错误出现在用户面前。我们必须重新启动服务器。

我为什么写作

然而,在这一点上,我希望重新创建问题 #1。我可以创建一个 容易死锁。

但这是我的问题:而在生产中,MSSQL 选择了一个 连接(SPID)作为“死锁受害者”,在我的测试环境中,死锁只是挂起......并且挂起并挂起。永远?我不确定,但我把它挂了一夜,早上它仍然挂着。

那么问题来了:当发生死锁时,如何让 sql server “选择死锁牺牲品”?

目前的尝试

我尝试通过 jdbc url 设置“lock_timeout”参数(“lockTimeout=5000”),但是我得到的消息与生产中不同(在测试中,“超过锁定请求超时期限。”而不是在生产中“事务(进程 ID 59)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。”)

关于问题 #2 的一些细节

我研究了这个“无法恢复交易”的问题,发现了一个 几件事:

错误的异常处理可能会导致此问题。例如:java代码确实 不关闭 Statement/PreparedStatement 和驱动程序的实现 的“连接”卡在错误/陈旧/旧的“交易 ID”中 jdbc 驱动程序升级可能会解决问题。

不过,现在我只想重新创建死锁并创建 sql server “选择一个死锁受害者”。

提前致谢!

附录 A. 技术环境

发展:

sql server 2005 SP3 (9.00.4035.00) 驱动:sqljdbc.jar 1.0版 Jboss 3.2.6 jdbc url:jdbc:sqlserver://;

生产:

sql server 2005 SP2 (9.00.3042.00) 驱动:sqljdbc.jar 1.0版 Jboss 3.2.6 jdbc url:jdbc:sqlserver://;

附录 B. 强制死锁的步骤

获取连接 A 获取连接 B 使用连接 A 运行 sql1 使用连接 B 运行 sql2 使用连接 B 运行 sql1 使用连接 A 运行 sql2

在哪里 sql1: 更新成员集 name = name + 'x' WHERE member_id = 71

sql2: 更新成员集 name = name + 'x' WHERE member_id = 72

【问题讨论】:

【参考方案1】:

JDBc 连接进入错误状态的原因解释在这里:The server failed to resume the transaction... Why?。您应该先升级到JDBC SQL driver v2.0。该链接还包含有关如何修复应用程序处理以避免这种情况的建议,最重要的是避免将 JDBC 事务 API 与本机 Transact-SQL 事务混合。

至于死锁重现:您没有在测试中重新创建死锁。您刚刚阻止等待事务提交。死锁是另一回事,SQL Server 选择受害者,您不必设置死锁优先级、锁定超时或任何其他内容。死锁优先级是一个完全不同的主题,用于在某些场景中选择受害者,例如高优先级与低优先级夜间批处理。

如果您想消除死锁,任何死锁调查都应该从了解死锁开始。 Profiler 中的Dedlock Graph Event Class 是完美的起点。通过死锁图信息,您可以查看发生死锁的资源以及涉及的语句。大多数情况下,解决方案是修复应用程序中的更新顺序(始终遵循相同的顺序)或修复访问路径(即添加索引)。

更新

UPDATE .. WHERE key IN (SELECT ...) 通常是死锁的,因为操作不是原子的。多个线程可以返回 same IN 列表,因为 SELECT 部分不锁定任何内容。这只是一个猜测,要正确验证您必须查看死锁信息。

要验证您的手工测试是否存在死锁,您应该验证阻塞 SPID 是否形成了循环。看SELECT session_id, blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id <> 0。如果结果包含一个循环(例如,A 被 B 阻止,B 被 A 阻止)并且服务器确实触发死锁,这是一个错误。但是,你会发现阻塞列表不会形成循环,会是A被B阻塞,B被C阻塞,C不在列表中,说明你做错了在复试中。

【讨论】:

死锁图信息很好,但提取和查看底层 XML(从文本列)提供了更多关于死锁的信息。 >正在运行什么查询?究竟是什么导致了僵局?在我的测试环境中,我运行了非常简单的查询: sql1: UPDATE principal SET name = name + '.' WHERE principal_id = 71 sql2: UPDATE principal SET name = name + '.' WHERE principal_id = 72 然后以交叉/交叉顺序执行它们,即没有任何提交。 connectionA sql1 connection B sql2 sql1 sql2 在生产中,我们的“有问题的查询”(“prodbad”)看起来像这样: UPDATE post SET lock_flag = ? WHERE thread_id IN (SELECT thread_id FROM POST WHERE post_id = ?)【参考方案2】:

您可以为会话指定死锁优先级 f 使用

SET DEADLOCK_PRIORITY LOW | MEDIUM | HIGH

有关详细信息,请参阅此MSDN 链接。

您也可以使用以下命令查看未结交易

DBCC OPENTRAN (db_name)

此命令可以帮助您确定导致死锁的原因。有关更多信息,请参阅MSDN。

【讨论】:

【参考方案3】:

正在运行什么查询?究竟是什么导致了死锁?

你说你有两个连接 A 和 B。A 运行 sql1 然后 sql2,而 B 运行 sql2 然后 sql1。那么,正在完成的工作(查询)是什么?更重要的是,交易在哪里?您使用的是什么隔离级别?什么打开/关闭交易? (是的,这会导致对您的驱动程序使用的异常处理提出质疑——如果他们没有检测并正确处理返回的“它不起作用”消息,那么您绝对需要将它们取出并射击它们——子弹或青霉素,你的电话。)

了解死锁背后的显式细节将使您能够重新创建它。我首先尝试在您的应用程序“下方”重新创建它——也就是说,在 SSMS 中打开两个窗口,并在必要时手动重新创建应用程序的操作。一旦你能做到这一点,就退后一步,在你的应用程序中复制它——当然,所有这些都在你的开发服务器上!

(一个想法——你的开发数据库是你的生产数据库的副本吗?如果开发数据库比生产数据库小几个数量级,那么你的查询可能是相同的,但 SQL 在“幕后”所做的事情将大不相同。 )

最后一个想法,SQL 会自动检测和处理死锁(我真的不认为你可以禁用它),如果你的运行在一夜之间,那么我认为你没有死锁,而只是一个传统的锁定/阻塞问题。

[现在发布此消息——我会查找内容,稍后再查看。] [稍后]

有趣——SQL Server 2005 精简版不检测死锁,它只检测超时。你没有在 Dev 中使用它,是吗?

我看不到“关闭”或以其他方式控制死锁超时期限的方法。就在上周,我遇到了死锁并搞砸了,然后一些任意测试表明死锁在 5 秒内(对于我们的开发服务器)被检测到并解决。看起来你的开发机器上没有死锁,只是阻塞。但是要意识到这些东西对于“坐在椅子上的 DBA”来说是很难分析的,当这个问题发生时,你真的需要坐下来认真分析系统内部发生的事情。

【讨论】:

【参考方案4】:

[这是对答案的回应。 UI 不允许在答案上使用更长的“cmets”]

正在运行什么查询?究竟是什么导致了死锁?

在我的测试环境中,我运行了非常简单的查询:

sql1: 更新主体 SET 名称 = 名称 + '.' WHERE principal_id = 71

sql2: 更新主体 SET 名称 = 名称 + '.' WHERE principal_id = 72

然后以交叉/交叉顺序执行它们,即没有任何提交。

连接A

 sql1

   connectionB

      sql2

      sql1

 sql2

对我来说,这似乎是一个基本的死锁示例。然而,如果这是一个“单纯的锁”,而不是一个死锁,请摒弃我的想法。

在生产中,我们的“有问题的查询”(“prodbad”)看起来像这样:

更新后 SET lock_flag = ? WHERE thread_id IN (SELECT thread_id FROM POST WHERE post_id = ?)

注意几点:

1) 这个“prod 问题查询”确实有效。 AFAIK 它有一个 一次死机

2) 我怀疑问题出在页面锁定上,即由于在事务中的其他地方读取而导致的悲观锁定

3) 我不知道这个事务在这个查询之前执行了什么 sql。

4 )此查询是“我可以在一个 sql 语句中做到这一点”的示例 处理,虽然对程序员来说似乎很聪明,但最终导致的 IO 比运行两个查询要多得多:

queryM:SELECT thread_id FROM POST WHERE post_id = ?

queryN: UPDATE post SET lock_flag = ? WHERE thread_id =

*>(一个想法——你的开发数据库是你的生产数据库的副本吗?

如果开发数据库比生产数据库小几个数量级,您的查询可能是相同的,但>SQL 在“幕后”所做的将大不相同。)*

在这种情况下,prod 和 dev db 不同。 “产品服务器”有大量数据。 “Dev db”的数据很少。查询非常不同。我想做的只是重新造成死锁。

*> 服务器无法恢复事务...为什么?你应该升级到 JDB

C SQL 驱动程序 v2.0 优先。*

谢谢。我们计划进行此更改。切换驱动程序会带来一点风险,所以我们需要运行一些测试..

回顾一下:

我有一个“好主意”来强制一个简单的死锁,看看我的连接是否“被破坏/软管/中断/等等”。然而,僵局的表现与生产中不同。

【讨论】:

以上是关于sql server 2005 死锁在生产中超时,而不是在测试环境中:为啥?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005 密钥死锁

诊断SQL Server 2005中的死锁

如何消除 SQL Server 2005 中的死锁?

SQL Server 2005 中的死锁!两个实时批量更新正在战斗。为啥?

SQL Server 2005:读取提交事务隔离级别中的键范围锁?

UPDATE 上的 SQL Server 死锁