web-dev-qa-db-ja.com

Hibernate:ロックを取得しようとしたときにデッドロックが見つかりました

プロジェクトでhibernateを使用していて、非常に単純なデータベース操作でランダムな見かけ上のデッドロックが発生しています。

スタックトレースの1つがあります: https://Gist.github.com/knyttl/8999006 –最初の例外がRollbackExceptionであることを混乱させるものそして、LockAquisition例外があります。

この問題は、同様の句で頻繁に発生します。

@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>

編集1:

  • 上記で書いたように、見かけ上のデッドロックが発生していた-それは間違っていて、「ロックを取得しようとしたときにデッドロックが見つかりました」だけが発生した。

EDIT2:

これはこれらのメソッドでも発生します-これらのニーズは@Transactionalで注釈されます:

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

デッドロックが頻繁に発生するため、アプリケーションの一部のスレッドが長時間ロックを保持しているように見えます。

アプリケーションの各スレッドは、データベースにアクセスしている間、独自のデータベース接続を使用するため、データベースの観点からは、2つのスレッドがデータベースロックを競合する2つの異なるクライアントになります。

スレッドが長期間ロックを保持し、それらを特定の順序で取得し、2番目のスレッドが同じロックを取得するが別の順序で取得する場合、デッドロックが発生することになります( ここ を参照)。この頻繁なデッドロックの原因の詳細については)。

また、読み取り操作でデッドロックが発生しているため、一部のスレッドも読み取りロックを取得しています。これは、スレッドが_REPEATABLE_READ_分離レベルまたはSERIALIZABLEでトランザクションを実行している場合に発生します。

これを解決するには、プロジェクトで_Isolation.REPEATABLE_READ_および_Isolation.SERIALIZABLE_の使用法を検索して、これが使用されているかどうかを確認してください。

別の方法として、デフォルトの_READ_COMMITTED_分離レベルを使用し、_@Version_でエンティティに注釈を付けて、代わりに optimistic Locking を使用して同時実行を処理します。

また、長時間実行されているトランザクションを特定しようとします。これは、_@Transactional_が間違った場所に配置され、トランザクションを1行ずつ実行するのではなく、バッチ処理の例でファイル全体の処理をラップする場合などに時々発生します。

これは、エンティティマネージャーとトランザクションの作成/削除とトランザクションの開始/コミット/ロールバックをログに記録するためのlog4j構成です。

_   <!-- 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. @ Transactionalを介してテーブルをロックする必要なしに、何らかの方法で更新クエリ(JPA /ネイティブ)を実行できますか?

更新クエリは、ネイティブクエリまたは [〜#〜] jpql [〜#〜] を介して可能です。

  1. @ Transactionalを使用せずに何らかの方法でセッションに入ることができますか?たとえば、スケジュールされたスレッドがエンティティのレイジーフィールドを読み取ろうとすると、 LazyInitializationException-メソッドに@Transactionalアノテーションが付けられていない場合、セッションはありません

_@Transactional_のないメソッドでは、クエリは自身のエンティティマネージャーで実行され、分離されたエンティティのみを返します。これは、クエリが実行された直後にセッションが閉じられるためです。

したがって、_@Transactional_を使用しないメソッドでの遅延初期化例外は正常です。それらを@Transactional(readOnly=true)に設定することもできます。

23

これはMySQLのエラーです。

デッドロックを解決して回避する最も簡単な方法は、アプリケーションで発生するDB操作を並べ替えることです。

以下のように、デッドロックは主に、複数のリソース/接続が反対の順序で複数のロックを取得しようとしたときに発生します。

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

両方の接続が同時に実行されるシナリオでは、接続1はkey(1)のロックを取得し、接続2はkey(2)のロックを取得します。その後、両方の接続は、他のユーザーがキーのロックを解放するのを待ちます。これによりデッドロックが発生します。

ただし、トランザクションの順序を少し調整すると、デッドロックを回避できます。

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

上記の再注文はデッドロックの証拠です。

デッドロックを回避する他の方法は、トランザクション管理メカニズムを持つことです。 トランザクション管理 Springはほとんどプラグアンドプレイです。さらに、デッドロック再試行ポリシーを設定できます。 Spring AOPを介した興味深いデッドロック再試行が見つかります here 。この方法では、デッドロックが発生した場合に再試行するメソッドに注釈を追加するだけです。

デッドロックに関するデバッグログで疑わしいステートメントを見つけるには、「show engine innodb status」診断を実行してみてください。また、デッドロックへの対処方法もご覧ください。

UPDATE:トランザクションDB操作でのデッドロックのシナリオ。

トランザクションデータベースでは、それぞれのトランザクション内の2つのプロセスが2行の情報を逆の順序で更新すると、デッドロックが発生します。たとえば、プロセスAは正確なタイムフレームで行1を更新してから行2を更新します。プロセスBは行2を更新してから行1を更新します。プロセスAはプロセスBが終了するまで行2の更新を終了できませんが、プロセスまで行1の更新を終了できません。フィニッシュ。どれだけの時間が経過しても、この状況が解決することはありません。このデータベース管理システムにより、通常、最も作業量の少ないプロセスのトランザクションが強制終了されます。

シシル

5
Shishir Kumar