在我的开发环境中,我寻求重新创建一个生产问题 面对MSSQL 2005.这个问题有两个部分:
问题
1)发生死锁,MSSQL选择一个连接(“连接X”)作为“受害者”。 2)所有后续尝试使用“连接X”失败(我们使用连接池)。 MSSQL说“服务器无法恢复交易”
在这两个中,#2如果更严重:因为“连接X”每个都被打击 “循环”试图重新使用“连接x”失败 - 而且神秘 “随机”错误出现在用户身上。我们必须重新启动服务器。
我为什么写
然而,此时,我希望重现问题#1。我可以创建一个 很容易陷入僵局。
但这是我的问题:在生产中,MSSQL选择一个 连接(SPID)作为'死锁牺牲品',在我的测试环境中,死锁只是挂起......然后挂起并挂起。永远?我不确定,但是我把它留在了一夜之间,它仍然挂在早上。
所以这里有一个问题:如何在发生死锁时让sql server“选择死锁受害者”?
尝试到目前为止
我尝试通过jdbc url(“lockTimeout = 5000”)设置“lock_timeout”参数,但是我得到了与生产中不同的消息(在测试中,“超出锁定请求超时时间”而不是生产中“事务(进程ID 59)在锁资源上与另一个进程陷入僵局,并被选为死锁牺牲品。“)
问题#2的一些细节
我研究了这个“无法恢复交易”的问题并找到了一个 很少的事情:
但是,现在,我只想重新创建一个死锁并创建sql server “选择一个死锁受害者”。
提前感谢!
附录A.技术环境
发展:
生产:
附录B.强制死锁的步骤
,其中 SQL1: 更新成员集名称=名称+'x'WHERE member_id = 71
SQL2: 更新成员集名称=名称+'x'WHERE member_id = 72
答案 0 :(得分:0)
您可以使用
为会话指定死锁优先级SET DEADLOCK_PRIORITY LOW | MEDIUM | HIGH
有关详细信息,请参阅此MSDN链接。
您还可以使用以下命令查看已打开的交易
DBCC OPENTRAN (db_name)
此命令可帮助您确定导致死锁的原因。有关详细信息,请参阅MSDN。
答案 1 :(得分:0)
正在运行的查询是什么?究竟是什么导致了僵局?
你说你有两个连接A和B. A运行sql1然后运行sql2,而B运行sql2然后运行sql1。那么,正在完成的工作(查询)是什么?更重要的是,交易在哪里?你使用什么隔离级别?什么打开/关闭交易? (是的,这会导致对驱动程序使用的异常处理提出质疑 - 如果他们没有检测到并正确处理返回的“它无法正常工作”消息,那么你绝对需要将它们取回并拍摄它们 - 子弹或青霉素,你的电话。)
了解死锁底层的显式细节将允许您重新创建它。我首先尝试在您的应用程序“下方”重新创建它 - 也就是说,在SSMS中打开两个窗口,并在必要时手动逐步重新创建应用程序的操作。一旦你可以做到这一点,退后一步并在你的应用程序中复制它 - 当然都在开发服务器上!
(思考 - 您的生产数据库的Dev数据库副本?如果Dev DB比Prod数量级小,您的查询可能是相同的,但SQL“幕后”的内容将大不相同。 )
最后一想,SQL会自动检测并处理死锁(我真的不认为你可以禁用它),如果你在一夜之间运行那么我认为你没有死锁,而只是一个传统的锁定/阻止问题。
[现在发布 - 要查看一些内容,稍后会再查看。]
[后来]
有趣 - SQL Server 2005精简版不会检测到死锁,它只会执行超时。你不是在Dev中使用它,是吗?
我认为无法“关闭”或以其他方式控制死锁超时时间。我上周就遇到了僵局,然后进行了一些任意测试,表明在5秒内检测到并解决了死锁(对于我们的开发服务器)。看起来你真的没有机器上的死机,只是阻塞。但是要意识到这些东西对于“扶手椅DBA”很难分析,你真的需要坐下来对这个问题发生时系统内部的情况进行一些认真的分析。
答案 2 :(得分:0)
此处给出了JDBc连接进入错误状态的原因的解释:The server failed to resume the transaction... Why?。您应该先升级到JDBC SQL driver v2.0。该链接还包含有关如何修复应用程序处理以避免这种情况的建议,最重要的是避免将JDBC事务API与本机Transact-SQL事务混合使用。
至于死锁repro:你没有在测试中重新创建死锁。您刚刚阻止等待事务提交。死锁是另一回事,SQL Server 将选择受害者,您不必设置死锁优先级,锁定超时或任何东西。死锁优先级是一个完全不同的主题,用于在某些情况下选择受害者,如高优先级与低优先级隔夜批处理。
任何死锁调查都应从了解死锁开始,如果你想消除它。 Profiler中的Dedlock Graph Event Class是一个完美的起点。使用死锁图信息,您可以看到发生死锁的资源以及涉及的语句。大多数情况下,解决方案是修复应用程序中的更新顺序(始终遵循相同的顺序)或修复访问路径(即添加索引)。
<强>更新强>
UPDATE .. WHERE key IN (SELECT ...)
通常是死锁,因为操作不是原子操作。多个线程可以返回相同的 IN列表,因为SELECT部分不会锁定任何内容。这只是一个猜测,要正确验证您必须查看死锁信息。
要验证针对死锁的手工测试,您应该验证阻塞SPID是否形成循环。看SELECT session_id, blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id <> 0
。如果结果包含一个循环(例如A被A阻塞,B被B阻塞),那么服务器不会触发死锁,这就是一个错误。但是,你会发现阻塞列表将不形成一个循环,将被B和B阻止的东西被C和C阻止而不在列表中,这意味着你做错了什么在重复试验中。
答案 3 :(得分:0)
[这是对答案的回应。用户界面不允许对答案进行更长时间的“评论”
正在运行的查询是什么?究竟是什么导致了僵局?
在我的测试环境中,我运行了非常简单的查询:
SQL1: UPDATE主要SET名称=名称+'。' WHERE principal_id = 71
SQL2: UPDATE主要SET名称=名称+'。' WHERE principal_id = 72
然后以chiastic / criss-cross顺序执行它们,即没有任何提交。
<强> connectionA 强>
sql1
connectionB
sql2
sql1
sql2
这对我来说似乎是一个死锁的基本例子。但是,如果这只是一个“锁定”,而不是僵局,请理解我的想法。
在制作中,我们的'有问题的查询'(“prodbad”)看起来很喜欢这个:
UPDATE post 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 =&lt;&gt;
*&gt;(一个想法 - 您的生产数据库的Dev数据库副本?
如果Dev DB比Prod的小几个数量级,那么你的查询可能是相同的但是&gt;“幕后”的SQL会有很大的不同。)*
在这种情况下,prod和dev db有所不同。 “产品服务器”拥有大量数据。 “Dev db”的数据很少。查询非常不同。我想做的就是重新制造僵局。
* GT;服务器无法恢复交易......为什么?你应该升级到JDB
C SQL驱动程序v2.0之前。*
感谢。我们计划这一变化。切换驱动程序会带来一点风险,因此我们需要运行一些测试..
回顾一下:
我有一个“聪明的主意”来强迫一个简单的僵局,看看我的连接是否“被打瞌睡/被软化/接受/等等”。然而,僵局的表现与生产中的不同。