2012-09-27 26 views
26

讓我們先忘掉Hibernate。假設我有兩個表A & B.兩個事務正在更新這兩個表中的相同記錄,但是txn 1更新B然後A,而txn 2更新A然後B.這是一個典型的死鎖示例。避免這種情況的最常見方法是預先定義獲取資源的順序。例如,我們應該更新表A然後B.Hibernate如何決定更新/插入/刪除的順序

回到Hibernate。當我們在一個會話中更新大量實體時,一旦我正在刷新會話,不同實體的更改將爲DB生成相應的插入/更新/刪除語句。 Hibernate是否有一些算法來決定實體之間的更新順序?如果不是,Hibernate用來防止第一段中描述的死鎖情況的方式是什麼?

如果Hibernate維護訂單,我如何知道或控制訂單?我不希望我的顯式更新與Hibernate的數據庫衝突,並導致死鎖。

回答

29

你描述的問題不是由數據庫處理的,根據我的經驗,Hibernate也不完全處理這個問題。

您必須採取明確措施避免它成爲問題。

Hibernate爲你做了一些工作。根據之前的答案,Hibernate確保在隔離的flush中插入,刪除和更新的順序可以確保它們以可實現的順序應用。見performExecutions(EventSource session)在AbstractFlushingEventListener類:

執行所有SQL(和二級緩存更新)在一個特殊的順序,使外鍵約束不能侵犯:

  1. 插入,他們的順序進行收集元件的
  2. 更新
  3. 缺失
  4. 插入收集元件的
  5. 個刪除,他們的順序進行

具有唯一約束知道這個順序是非常重要的當,特別是如果你想更換一個一對多的孩子(刪除舊/插入新的),但舊的和新的孩子都有相同的獨特約束(例如,同一電子郵件地址)。在這種情況下,您可以更新舊條目,而不是刪除/插入,也可以在刪除後進行刷新,然後繼續插入。有關更詳細的示例,您可以檢查this article

請注意,它沒有指定更新的順序。檢查Hibernate代碼會讓我認爲更新順序將取決於實體添加到持久性上下文的順序,即不是它們更新的順序。這可能在你的代碼中是可以預測的,但是閱讀Hibernate代碼並沒有讓我覺得我會依賴這個順序。

有三種解決方案,我能想到的:

  1. 嘗試設置hibernate.order_updates真正。當更新同一表中的多行時,這應有助於避免死鎖,但不會有助於跨多個表的死鎖。
  2. 在進行任何更新之前,讓您的交易對其中一個實體執行PESSIMISTIC_WRITE鎖定。您使用哪個實體取決於您的具體情況,但只要您確保在出現死鎖風險的情況下一致地選擇實體,就會阻止事務的其餘部分,直到獲得鎖定。
  3. 編寫代碼以捕獲發生的死鎖並以合理的方式重試。管理死鎖重試的組件必須位於當前事務邊界之外。這是因爲失敗的會話必須關閉並且關聯的事務回滾。在this article中,您可以找到自動重試AOP方面的示例。
+1

請接受我的道歉,因爲不明原因,我錯過了這個答案:)我相信你在Hibernate代碼中的考試結果實際上給出了迄今爲止最可信的答案。特別是針對第1點和第2點的好建議 –

-1

關於第一個例子 - 這種東西是由數據庫處理的(閱讀關於事務隔離級別和數據庫鎖定策略的更多信息)。有很多不同的方法可以處理。

至於休眠,要的javadoc org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(EventSource的)說:

執行所有SQL和二級緩存更新,在一個特殊的順序,使外鍵約束不容侵犯:

  1. 插入,他們的順序進行
  2. 收集要素的更新刪除
  3. 插入收集要素的
  4. 刪除,按它們執行的順序

我假設這是Hibernate所執行的SQL查詢的唯一優化。剩下的問題由數據庫處理。

+0

我相信第一個例子的東西大多數不能被數據庫處理。對於隔離級別,從我的理解來看,更多的是關於一個事務的更改如何對其他人可見。但是,即使是最低的隔離級別,寫入同一行的兩個事務仍然會鎖定。因此,如果一個txn更新A記錄然後B,而另一個txn更新B記錄A,那麼它仍然會產生死鎖。 DBMS可以做的最好的事情是檢測潛在的死鎖發生。我對你提到的Hibernate SQL執行順序感到驚訝,但是我所關心的大約是實體之間的更新順序 –

+0

。如果Hibernate沒有對此進行任何處理並且假設可以由DB處理(這顯然是不現實的),那將是相當令人驚訝的。 –

+0

Oracle以某種方式處理死鎖「自動檢測並通過回滾與事務相關的語句來解決死鎖檢測到死鎖「:http://www.oracle-base.com/articles/misc/deadlocks.php。這可能會導致JDBC異常,因此您可以在處理異常的代碼中添加一些重試邏輯。 – Potejciak