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) {
    }
}
相关文章
相关标签/搜索