SQL死锁问题

时间:2010-07-15 17:03:29

标签: sql sql-server-2005 database-deadlocks

这两个语句的关系数据库是否可能出现死锁?我正在尝试简化我的问题和示例 - 请假设这些选择,我认为通常只需要可共享的读锁定,现在需要独占的读锁:

Concurrent Connection 1:
SELECT {...}
FROM A 
JOIN B ON {...}

Concurrent Connection 2:
SELECT {...}
FROM B 
JOIN A ON {...}

那就是,连接的顺序是否重要? SQL中的单个语句是原子的吗?首先锁定A然后在第一个语句中B和B先锁定然后在第二个语句中锁定A?

我认为不是 - 我的直觉告诉我,无论多么复杂,这样的两个单一陈述都不会陷入僵局。我相信整个语句是分析的,并且需要锁定的资源是使用一些确定性的全局顺序(即按字母顺序)锁定的。但我需要的不仅仅是对此的直觉 - 我无法想出一种证明它的方法,而且我无法找到它。

我对MS SQL 2005很感兴趣,但我不认为这个问题是特定于实现的。

其次:由于它与MS SQL有关,我还想知道Common Table Expressions也有这种保证--CTE主要是语法上的好处(+递归),由引擎合并为传统的单个语句。

3 个答案:

答案 0 :(得分:9)

SELECT不能与其他SELECT死锁,因为它们只获取共享锁。你说我们应该考虑这些SELECT现在'需要独占读锁',但我们不可能考虑因为1)没有exlusive read lock和2)读取不获取独占锁。

但是你确实提出了一个更普遍的问题,简单的陈述是否会陷入僵局。答案是肯定的,响亮的。锁是在执行时获取的,不是预先分析的,而是按某种顺序排序然后获取。引擎不可能预先知道所需的锁,因为它们依赖于磁盘上的实际数据,并且读取引擎需要的数据......锁定数据。

由于索引访问顺序不同,简单语句(SELECt与UPDATE或SELECT与DELETE)之间的死锁非常常见,非常容易调查,诊断和修复。但请注意,始终涉及写入操作,因为读取不能相互阻塞。对于此讨论,将UPDLOCK或XLOCK提示添加到SELECT应视为写入。您甚至不需要JOIN,二级索引可能会引入导致死锁的访问顺序问题,请参阅Read/Write Deadlock

最后,撰写SELECT FROM A JOIN B或撰写SELECT FROM B JOIN A完全无关紧要。查询优化器可以根据需要重新排列访问顺序,查询的实际文本不会以任何方式强加执行顺序。

<强>更新

  

那么我们如何构建一个将军呢?   针对READ COMMITTED的策略   “多实体”数据库即   没陷入僵局?

我担心没有千篇一律的食谱。解决方案将视具体情况而定。最终,在数据库应用程序中,死锁是生活中的事实。我知道这可能听起来很荒谬,因为“我们登陆月球但我们无法编写正确的数据库应用程序”,但有很多因素在起作用,这几乎可以保证应用程序最终会遇到死锁。幸运的死锁是 最容易处理错误,简单再次读取状态,应用逻辑,重新写入新状态。现在有人说,有一些好的做法可以大大减少死锁的频率,直到它们几乎消失了:

  • 尝试为 Writes 创建一致的访问模式。有明确定义的规则说明“交易应始终按此顺序排列表:Customers - &gt; OrderHeaders - &gt; OrderLines“。请注意,必须在事务中遵守该订单。基本上,在架构中对所有表进行排名,并指定所有更新必须按排名顺序进行。这最终归结为编写代码的个人贡献者的代码规则,因为它必须确保它写入事务中的正确顺序更新。
  • 减少写入的持续时间。通常的智慧是:在事务开始时执行所有读取(读取现有状态),然后处理逻辑并计算新值,然后在事务结束时写入所有更新。避免使用类似'read-&gt; write-&gt; logic-&gt; read-&gt; write'的模式,而不是'read-&gt; read-&gt; logic-&gt; write-&gt; write'。当然,真正的工艺在于如何处理实际的,真实的,个别的情况,显然一个必须必须在交易中进行写入。这里必须特别说明一种特定类型的事务:由队列驱动的事务,它通过非常定义通过从队列中出列(=写入)来启动它们的活动。这些应用程序总是很难编写并且容易出错(特别是死锁),幸运的是有很多方法可以做到,请参阅Using tables as Queues
  • 减少读取量。表扫描是 最常见的死锁原因。正确的索引不仅可以消除死锁,还可以提高过程中的性能。
  • Snapshot isolation。这是你在避免死锁方面最接近免费午餐的事情。我故意把它放在最后,因为它可能掩盖其他问题(比如不正确的索引)而不是修复它们。

尝试用LockCustomerByXXX方法解决这个问题我恐怕不行。悲观锁定不会扩展。如果您想获得任何体面的表现,Optimistic concurrency更新 即可。

答案 1 :(得分:0)

据我所知,你是对的:SQL引擎计算出它需要做什么(可能是在解析查询时),锁定所有必需的资源,执行查询,然后解锁它们。

答案 2 :(得分:0)

读取不会相互死锁。你也必须有一些写作。

您可以采取措施减少死锁数量。例如,仅在支持行锁定的平台上的聚簇索引的末尾插入并避免更新记录。啊哈,现在Facebook的UI更有意义。

有时比处理死锁更容易处理死锁。服务器将失败并报告回来,您可以重试。