복붙노트

[SPRING] "잠금을 얻으려고 할 때 교착 상태가 발견되었습니다. 트랜잭션을 다시 시작하십시오. "

SPRING

"잠금을 얻으려고 할 때 교착 상태가 발견되었습니다. 트랜잭션을 다시 시작하십시오. "

내 응용 프로그램 (자바 스프링 코어)은 동시에 실행되고 db에 액세스하는 여러 스레드가 있으며 일부 피크 타임에는 예외가 발생합니다.

07:43:33,400 WARN  [org.hibernate.util.JDBCExceptionReporter] SQL Error: 1213, SQLState: 40001
07:43:33,808 ERROR [org.hibernate.util.JDBCExceptionReporter] Deadlock found when trying to get lock; try restarting transaction
07:43:33,808 ERROR [org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.LockAcquisitionException: could not insert: [com.xminds.bestfriend.frontend.model.Question]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:107)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2436)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2856)
    at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:147)
    at com.xminds.bestfriend.consumers.Base.onMessage(Base.java:96)
    at org.springframework.jms.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:339)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:535)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:495)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:467)
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:325)
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:263)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1058)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1050)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:947)
    at java.lang.Thread.run(Thread.java:662)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1065)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4074)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2719)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2450)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2371)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2355)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:46)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2416)
    ... 25 more

내 코드가 보인다.

try
{
       this.consumerTransactionTemplate.execute(new TransactionCallbackWithoutResult(){

                    @Override
                    protected void doInTransactionWithoutResult(
                            TransactionStatus status)
                    {
                        process();
                    }

                });

  }
  catch(Exception e){
     logger.error("Exception occured " , e);
      //TODO: Exception handling
  }

해결법

  1. ==============================

    1.MySQL의 InnoDB 엔진은 행 수준의 잠금 기능을 제공하므로 코드가 단일 행을 삽입하거나 업데이트 할 때 (특히 업데이트되는 테이블에 여러 개의 인덱스가있는 경우)에도 교착 상태가 발생할 수 있습니다. 교착 상태로 인해 트랜잭션이 실패 할 경우 트랜잭션을 다시 시도하기 위해이 문제를 해결하기위한 코드를 설계하는 것이 가장 좋습니다. MySQL 교착 상태 진단 및 가능한 해결 방법에 대한 유용한 정보가 여기에 있습니다.

    MySQL의 InnoDB 엔진은 행 수준의 잠금 기능을 제공하므로 코드가 단일 행을 삽입하거나 업데이트 할 때 (특히 업데이트되는 테이블에 여러 개의 인덱스가있는 경우)에도 교착 상태가 발생할 수 있습니다. 교착 상태로 인해 트랜잭션이 실패 할 경우 트랜잭션을 다시 시도하기 위해이 문제를 해결하기위한 코드를 설계하는 것이 가장 좋습니다. MySQL 교착 상태 진단 및 가능한 해결 방법에 대한 유용한 정보가 여기에 있습니다.

    Spring에서 AOP를 통한 교착 상태 재 시도의 흥미로운 구현은 여기에서 볼 수있다. 이렇게하면 교착 상태가 발생했을 때 다시 시도하려는 메소드에 주석을 추가하기 만하면됩니다.

  2. ==============================

    2.Emir의 대답은 훌륭하고 당신이 얻고있는 문제를 설명합니다. 그러나 나는 봄 재 시도를 시도하는 것이 좋습니다.

    Emir의 대답은 훌륭하고 당신이 얻고있는 문제를 설명합니다. 그러나 나는 봄 재 시도를 시도하는 것이 좋습니다.

    주석 (들)을 통해 재시도 패턴을 구현하는 훌륭한 프레임 워크입니다.

    예:

     @Retryable(maxAttempts = 4, backoff = @Backoff(delay = 500))
     public void doSomethingWithMysql() {
       consumerTransactionTemplate.execute(
                 new TransactionCallbackWithoutResult(){
                    @Override
                    protected void doInTransactionWithoutResult(                 
                          TransactionStatus status)
                    {
                        process();
                    }
    
                });
     } 
    

    어떤 예외가 발생하면 백 오프 정책이 500ms 인 doSomethingWithMysql () 메서드의 최대 4 배를 다시 시도 (호출)합니다.

  3. ==============================

    3.이런 종류의 오류가 발생하면 "교착 상태가 감지되었습니다". 쿼리 실행을 검사하고 두 개 이상의 동시 트랜잭션으로 인해 교착 상태가 발생할 수 있는지 확인해야합니다.

    이런 종류의 오류가 발생하면 "교착 상태가 감지되었습니다". 쿼리 실행을 검사하고 두 개 이상의 동시 트랜잭션으로 인해 교착 상태가 발생할 수 있는지 확인해야합니다.

    이러한 트랜잭션은 교착 상태를 피하기 위해 동일한 순서로 데이터베이스 잠금을 획득해야합니다.

  4. from https://stackoverflow.com/questions/17747906/getting-deadlock-found-when-trying-to-get-lock-try-restarting-transaction by cc-by-sa and MIT license