簡體   English   中英

Hibernate:嘗試獲取鎖定時發現死鎖

[英]Hibernate: Deadlock found when trying to obtain lock

我在我的項目中使用hibernate,我得到隨機的表觀死鎖,用於非常簡單的數據庫操作。

有一個堆棧跟蹤: https ://gist.github.com/knyttl/8999006 - 讓我感到困惑的是,第一個Exception是RollbackException,然后是LockAquisition Exceptions。

問題經常出現在類似的條款中:

@Transactional
public void setLastActivity() {
    User user = em.findById(...);
    user.setLastActivity(new Date());
    em.merge(user);
    em.flush();
}

我很困惑,因為我不知道它是Hibernate,MySQL還是C3P0的問題。

我的Hibernate配置:

            <prop key="hibernate.dialect">${database.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${database.structure}</prop>
            <prop key="hibernate.connection.url">${database.connection}</prop>
            <prop key="hibernate.connection.username">${database.username}</prop>
            <prop key="hibernate.connection.password">${database.password}</prop>
            <prop key="hibernate.connection.driver_class">${database.driver}</prop>
            <prop key="hibernate.connection.shutdown">true</prop>
            <prop key="hibernate.connection.writedelay">0</prop>
            <prop key="hibernate.connection.characterEncoding">UTF-8</prop>
            <prop key="hibernate.connection.charSet">UTF-8</prop>
            <prop key="hibernate.show_sql">${database.show_sql}</prop>
            <prop key="hibernate.format_sql">false</prop>
            <prop key="hibernate.ejb.metamodel.generation">disabled</prop>
            <!-- Use the C3P0 connection pool provider -->
            <prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
            <prop key="hibernate.c3p0.min_size">0</prop>
            <prop key="hibernate.c3p0.max_size">50</prop>
            <prop key="hibernate.c3p0.timeout">120</prop>
            <prop key="hibernate.c3p0.max_statements">0</prop>
            <prop key="hibernate.c3p0.max_statementsPerConnection">0</prop>
            <prop key="hibernate.c3p0.maxStatementsPerConnection">0</prop>
            <prop key="hibernate.c3p0.idle_test_period">120</prop>
            <prop key="hibernate.c3p0.acquire_increment">1</prop>
            <prop key="hibernate.c3p0.numHelperThreads">8</prop>

EDIT1:

  • 我在上面寫道發生了表觀死鎖 - 這是錯誤的,只有“試圖獲取鎖定時發現死鎖”發生。

EDIT2:

這也發生在這些方法上 - 那些需要用@Transactional注釋的NEEDS:

@Transactional
public void setLastActivity() {
    em.insertNative("table")
           .values(...)
           .execute();
}

因為死鎖頻繁發生,所以看起來應用程序的某些線程會持續鎖定一段時間。

應用程序中的每個線程在訪問數據庫時都將使用它自己的數據庫連接/連接,因此從數據庫的角度來看,兩個線程是兩個不同的客戶端,它們爭用數據庫鎖。

如果一個線程長時間持有鎖並以某種順序獲取它們,並且第二個線程以不同的順序獲取相同的鎖,則必然會發生死鎖(有關此頻繁死鎖原因的詳細信息,請參見此處) )。

在讀取操作中也會發生死鎖,這意味着某些線程也在獲取讀鎖定。 如果線程在REPEATABLE_READ隔離級別或SERIALIZABLE中運行事務,則會發生這種情況。

要解決此問題,請嘗試在項目中搜索Isolation.REPEATABLE_READIsolation.SERIALIZABLE用法,以查看是否正在使用它。

作為替代方法,使用默認的READ_COMMITTED隔離級別並使用@Version注釋實體,以使用樂觀鎖定來處理並發。

同時嘗試識別長時間運行的事務,有時當@Transactional放在錯誤的位置時會發生這種情況,例如在批處理的示例中處理整個文件,而不是逐行執行事務。

這是一個log4j配置,用於記錄實體管理器和事務begin / commit / rollback的創建/刪除:

   <!-- spring entity manager and transactions -->
<logger name="org.springframework.orm.jpa" additivity ="false">
    <level value="debug" />
    <appender-ref ref="ConsoleAppender" />
</logger >
<logger name="org.springframework.transaction" additivity ="false">
    <level value="debug" />
    <appender-ref ref="ConsoleAppender" />
</logger >
  1. 我可以以某種方式執行更新查詢(JPA / Native)而無需通過@Transactional鎖定表嗎?

可以通過本機查詢或JPQL進行更新查詢。

  1. 我可以不使用@Transactional進入會話嗎? 例如,如果該方法未使用@Transactional注釋,則調度線程會嘗試將實體上的Lazy字段讀取為LazyInitializationException - 無會話

在沒有@Transactional方法中,查詢將在它自己的實體管理器中執行,並且只返回分離的實體,因為在運行查詢后會立即關閉會話。

所以沒有@Transactional方法中的延遲初始化異常是正常的。 您也可以將它們設置為@Transactional(readOnly=true)

這是MySQL的錯誤。

解決和避免死鎖的最簡單方法是重新排序應用程序中發生的數據庫操作。

死鎖主要發生在多個資源/連接嘗試以相反的順序獲取多個鎖時,如下所示:

connection 1: locks key(1), locks key(2);
connection 2: locks key(2), locks key(1);

在兩個連接同時執行的情況下,連接1將獲得密鑰(1)上的鎖定,以及密鑰(2)上的連接2。 之后,兩個連接都將等待其他連接釋放鎖上的鎖。 這導致死鎖。

但是,按照事務的順序稍微調整一下,就可以避免死鎖。

connection 1: locks key(1), locks key(2);
connection 2: locks key(1), locks key(2);

以上重新訂購是死鎖證明。

避免死鎖的其他方法是使用事務管理機制。 Spring的事務管理幾乎是即插即用的。 此外,您可以實施死鎖重試策略。 通過Spring AOP進行有趣的死鎖重試可以在這里找到。 這樣,您只需要將注釋添加到要在死鎖情況下重試的方法。

有關死鎖的更多調試日志以找出可疑的語句,請嘗試運行“show engine innodb status”診斷。 此外,您可以看看如何應對死鎖

更新:事務數據庫操作中死鎖的方案。

在事務數據庫中,當其自身事務中的每個進程更新兩行信息但順序相反時,就會發生死鎖。 例如,進程A在確切的時間幀進程B中更新第1行然后第2行更新第2行然后第1行。進程A無法完成更新第2行直到進程B完成,但是它無法完成更新第1行直到進程完成。 無論允許多長時間通過,這種情況永遠不會自行解決,因為這種數據庫管理系統通常會終止進行最少量工作的進程的事務。

Shishir

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM