2010-09-02 21 views
3

我使用Hibernate和Joined-SubClasses將類層次結構映射到 數據庫。不幸的是,當一個對象被更新而另一個線程試圖加載相同的對象時,這會導致死鎖。使用將 映射到單個表的對象,這不成問題。這似乎是MSSQL 在類層次結構的表上獲取鎖的方式造成的。Hibernate與MSSQL加入子類死鎖

當Hibernate從數據庫加載一個對象時,它使用SELECT與JOIN:

SELECT ... 
FROM 
    subclass 
    LEFT JOIN class 
     ON ... 
WHERE ... 

當Hibernate更新該子類它的一個目的:

UPDATE 
    class 
SET ... 
WHERE ... 

UPDATE 
    subclass 
SET ... 
WHERE ... 

的問題是,如果一個對象在兩個更新語句之間加載,它會導致死鎖。 SELECT語句似乎鎖定了另一個 之後的兩個表。那麼,什麼似乎發生的是:

  1. 線程1個負載的目標和地方共享的兩個表
  2. 線程1執行在類表中的UPDATE語句鎖和升級的類表 鎖獨佔鎖。
  3. 線程2試圖通過執行SELECT語句加載同一個對象,它 地方上的子類表的共享鎖,然後等待直到在類表中的排他 鎖被釋放
  4. 線程1執行UPDATE語句對於子類表,它希望 將其在子類表上的鎖升級爲排它鎖,但 表已被線程2鎖定,該線程正在等待線程1
  5. 線程2由於死鎖而中止線1

死鎖圖看起來像這樣:Deadlock Graph

這些對象經常更新安靜,並且這會導致始終造成死鎖,甚至在加載單個對象時甚至會導致 。我也嘗試用 HSQLDB重現問題,但是它不會死鎖,HSQLDB似乎要麼將兩個表都鎖定爲 一次,要麼等待它可以同時鎖定兩個表,所以這似乎是一個問題,只有 發生在MSSQL中。

如果不修改 模式(索引除外),用Hibernate避免這個問題的解決方案是什麼?

+0

我將Hibernate放在一邊問你的問題,這實際上是一個比Hibernate問題更多的MSSQL問題(如果適當標記它,你可能會獲得更多的牽引力)。無論如何你正在使用什麼事務隔離? PS:甚至不嘗試重現HSQLDB的問題(不確定使用的是哪個版本,但HSQLDB 1.8.2僅支持READ_UNCOMMITTED隔離級別,因此情況有很大不同)。 – 2010-09-02 15:02:24

+0

也許,但加載一個對象的事實在兩個表上創建了一個連接,並且對象的更新創建了2個獨立的UPDATE語句來自Hibernate。如果我離開Hibernate,那麼解決方案可能是在單獨的事務中運行這兩個更新,或者更改SELECT語句。但這是我不能做的事情,因爲這些語句是由Hibernate生成的。 我首先使用HSQLDB 1.8.x進行了測試,注意到它不起作用,然後切換到2.0,這延遲了線程2的SELECT語句,直到線程1的事務被提交爲止。 – Reboot 2010-09-02 15:45:55

+0

陳述的產生與否與事實無關,這是我的觀點。只要提出一個關於**這個**特定場景的問題(順便說一句,在單獨的查詢中運行更新不會)。但隨時忽略我的建議:) – 2010-09-02 19:50:17

回答

0

在我看來,這些更新需要在單個事務中以原子方式完成。不幸的是,我沒有很多關於Hibernate的背景知識,所以我會把它留給其他人來指出你在那裏的正確方向。

+0

它們在一次交易中執行。如果不是,他們不會導致其他交易陷入僵局。兩個線程的活動都在一個事務中完成。也許我應該更清楚一點。 – Reboot 2010-09-02 12:43:36

1

你打開了SQL Server deadlock trace flags 1204 or 1222?這將有助於準確識別造成死鎖的資源。有關更多信息,請參閱Detecting and Ending Deadlocks上的MSDN文章。

這些表上有索引嗎?如果是這樣,如果應用程序獲取聚集索引上的鎖,則會發生死鎖,然後嘗試通過查找非聚集索引來獲取同一個表上的更多鎖。

+1

我已經使用SQL Management Studio Profiler創建了一個死鎖圖並將其添加到問題的圖形中。在這兩個表上只有一行被訪問,對我來說,看起來select語句按照與UPDATE語句相反的順序鎖定兩個表。每個表中只有一行被訪問。我不知道索引如何幫助改變這一點。 – Reboot 2010-09-03 08:15:33