Pytanie Pierwsze "Zakleszczenie znalezione podczas próby uzyskania blokady; spróbuj ponownie uruchomić transakcję "


Moja aplikacja (java spring-core) ma kilka wątków działających równolegle i uzyskujących dostęp do db, otrzymuję wyjątek w niektórych peaktime

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

Mój kod wygląda

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

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

                });

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

21
2017-07-19 13:54


pochodzenie




Odpowiedzi:


Mechanizm InnoDB w MySQL ma blokowanie na poziomie wiersza, co może prowadzić do zakleszczenia nawet wtedy, gdy twój kod wstawia lub aktualizuje pojedynczy wiersz (szczególnie jeśli w tabeli aktualizowanych jest kilka indeksów). Najlepiej jest zaprojektować kod dookoła tego, aby ponowić transakcję, jeśli zawiedzie z powodu zakleszczenia. Dostępne są przydatne informacje na temat zakleszczenia zakleszczenia MySQL i możliwych obejść tutaj.

Interesująca jest realizacja impasu ponownego zaklęcia za pośrednictwem AOP na wiosnę tutaj. W ten sposób wystarczy dodać adnotację do metody, którą chcesz ponowić w przypadku zakleszczenia.


22
2017-07-19 14:35





Odpowiedź Emira jest świetna i opisuje problem, który dostajesz. Jednak proponuję ci spróbować spróbuj ponownie.

Jest to znakomita platforma, która implementuje wzorzec ponawiania za pomocą adnotacji.

Przykład:

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

            });
 } 

W przypadku jakiegokolwiek wyjątku, będzie ponawiał próbę (wywołanie) do 4 razy większej niż metoda doSomethingWithMysql () z zasadą opóźnienia wynoszącą 500ms


6
2017-07-06 13:24





Kiedy napotykasz tego rodzaju błąd "wykryto zakleszczenie". Należy sprawdzić wykonanie kwerend i sprawdzić, czy dwie lub więcej jednoczesnych transakcji może spowodować zakleszczenie.

Te transakcje powinny uzyskać blokady baz danych w tej samej kolejności, aby uniknąć zakleszczenia.


-1
2018-02-07 07:39



Co zrobić, jeśli problem nie występuje w kodzie, ale w systemie współbieżnym, który próbuje uzyskać dostęp do tego samego zasobu w tym samym czasie? - Xtreme Biker