spring聲明式事務@Transactional後置,前置處理

 

想必很多人遇到過這樣子的場景,但願在spring的事務完成後do something...java

前言:mysql

---------------------------------------------------------------------------------------------redis

我遇到的場景是,但願在搶購方法上使用redis setnx簡單的作一下鎖,來防止重複提交spring

步驟所有於@Transactional do something()內sql

一、使用userId+搶購專屬id 爲key  嘗試setnx 若是setnx成功,執行2ide

二、給key設置10秒的expire.net

三、crudcode

四、finally塊中執行刪除key事務

以上是個人一個防止重提提交的簡單辦法,爲了怕setnx死鎖,因此給key設置了expire,因爲步驟3中有查詢用戶是否已參與搶購的判斷,相似與簡單的樂觀鎖,因此覺得本方案可行get

但上線後發現,仍是會有部分用戶會存在重複搶購的問題,所以判斷本方案存在問題。想了一下,因爲@Transactional加在do something()上,因此可能存在問題以下:

    用戶的操做一,拿到了setnx,又重複操做二,三,而操做一又恰好很快的執行完,這個以後finally刪除了key,因此操做二,三都有可能成功操做,而因爲setnx在@Transactional do something()內部,而@Transactional採用默認事務(mysql rr),所以形成了重複搶購的問題

-----------------------------------------------------------------------------------------------

應急解決方案

一、將finally中步驟4,刪除key的操做去掉

這個方案雖然是解決了線上的問題,可是可能存在如下問題(我能想到的就這個)

一、若是用戶操做一這個事務處理爲11秒,這個時候操做二進來了,那麼就又會存在重複搶購的問題。

二、線上存在大量redis 10秒後消失的key

--------------------------------------------------------------------------------------------------------

其餘解決方案:

也就是咱們標題講到的spring聲明式事務@Transactional後置

後置方案一

一、在spring聲明式事務@Transactional 方法do something()添加以下代碼,在spring事務提交後再delete 對應的key

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
    @Override
    public void afterCommit() {
        redisTemplate.delete(key);
        super.afterCommit();
    }
});

二、若是出現了異常,在catch中同樣執行delete key的操做

(若是方案有任何問題,還請拍磚,本人仍是小菜,但願有大神指點)

----------------------------------------------------------------------------------------------------------------

後置方法二:

使用spring自帶的TransactionTemplate ,手動提交事務

----------------------------------------------------------------------------------------------------------------

後置方法三:

與方法一相似

extends TransactionSynchronizationAdapter implements AfterCommitExecutor

 

相似可能用到spring事務後置處理的場景頗有不少,固然這裏也能夠處理前置,支持的操做以下代碼

public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered {
    public TransactionSynchronizationAdapter() {
    }

    public int getOrder() {
        return 2147483647;
    }

    public void suspend() {
    }

    public void resume() {
    }

    public void flush() {
    }

    public void beforeCommit(boolean readOnly) {
    }

    public void beforeCompletion() {
    }

    public void afterCommit() {
    }

    public void afterCompletion(int status) {
    }
}
相關文章
相關標籤/搜索