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 中的死锁!两个实时批量更新正在战斗。为啥?