Getting “Deadlock found when trying to get lock; try restarting transaction”

后端 未结 5 1714
一个人的身影
一个人的身影 2020-12-07 21:11

My Application(java spring-core) has several threads running concurrently and accessing db, I am getting exception in some peaktime

07:43:33,400 WARN  [org.         


        
相关标签:
5条回答
  • 2020-12-07 21:14

    When you face this kind of error "deadlock detected". You should inspect your queries execution and verify if two or more concurrent transactions can cause a deadlock.

    These transactions should acquire database locks in the same order in order to avoid deadlock.

    0 讨论(0)
  • 2020-12-07 21:18

    If you are using JPA/Hibernate then simple just follow below steps to avoid dead lock. Once you have acquired the lock, don't made any call on db with same id anywhere in the transaction (I mean to say you should not get entity again on sameid), on locking object you modify and save no issues.

    service level:-

    employee=empDao.getForUpdate(id);
    

    Dao level:-

    public employee getForUpdate(String id)
    return mySqlRepository.getForUpdate(id)
    

    Repository(interface):-

    @Lock(LockModeType.PESSIMITSIC_WRITE)
    @Query("select e from employee e where id=?1")
    public employee getForUpdate(String id)
    
    0 讨论(0)
  • 2020-12-07 21:33

    MySQL's InnoDB engine sports row-level locking, which can lead to deadlocks even when your code is inserting or updating a single row (specially if there are several indexes on the table being updated). Your best bet is to design the code around this in order to retry a transaction if it fails due to a deadlock. Some useful info about MySQL deadlock diagnose and possible workarounds is available here.

    An interesting implementation of deadlock retry via AOP in Spring is available here. This way you just need to add the annotation to the method you want to retry in case of deadlock.

    0 讨论(0)
  • 2020-12-07 21:37

    Emir's answer is great and it describes the problem that you are getting. However I suggest you to try spring-retry.

    It's a brilliant framework that implements the retry pattern via annotation(s).

    Example:

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

    In case of exception, it will retry (call) up to 4 times the method doSomethingWithMysql() with a backoff policy of 500ms

    0 讨论(0)
  • 2020-12-07 21:39

    Here is an example with plain Spring and no extra frameworks.

        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); // autowired
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); // this increases deadlocks, but prevents multiple simultaneous similar requests from inserting multiple rows
        Object o = transactionTemplate.execute(txStatus -> {
                for (int i=0; i<3; i++) {
                    try {
                        return findExistingOrCreate(...);
                    } catch (DeadlockLoserDataAccessException e) {
                        Logger.info(TAG, "create()", "Deadlock exception when trying to find or create. Retrying "+(2-i)+" more times...");
                        try { Thread.sleep(2^i*1000); } catch (InterruptedException e2) {}
                    }
                }
                return null;
        });
        if (o == null) throw new ApiException(HttpStatus.SERVICE_UNAVAILABLE, "Possible deadlock or busy database, please try again later.");
    

    Using serializable transaction isolation level is specific to my situation because it converts SELECT to SELECT ... IN SHARE MODE / SELECT ... FOR UPDATE and locks those rows. The findExistingOrCreate() is doing a lot of complicated searching for existing rows and auto-generating names and checking for bad words, etc. When many of the same request came in at the same time, it would create multiple rows. With the serializable transaction isolation level, it is now indempotent; it now locks the rows, creates a single row, and all subsequent requests return the new existing row.

    0 讨论(0)
提交回复
热议问题