spring声明式事务@Transactional后置,前置处理

 

想必不少人遇到过这样子的场景,希望在spring的事务完成后do something...

前言:

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

我遇到的场景是,希望在抢购方法上使用redis setnx简单的做一下锁,来防止重复提交

步骤全部于@Transactional do something()内

1、使用userId+抢购专属id 为key  尝试setnx 如果setnx成功,执行2

2、给key设置10秒的expire

3、crud

4、finally块中执行删除key

以上是我的一个防止重提提交的简单办法,为了怕setnx死锁,所以给key设置了expire,由于步骤3中有查询用户是否已参与抢购的判断,类似与简单的乐观锁,所以以为本方案可行

但上线后发现,还是会有部分用户会存在重复抢购的问题,因此判断本方案存在问题。想了一下,由于@Transactional加在do something()上,所以可能存在问题如下:

    用户的操作一,拿到了setnx,又重复操作二,三,而操作一又刚好很快的执行完,这个之后finally删除了key,所以操作二,三都有可能成功操作,而由于setnx在@Transactional do something()内部,而@Transactional采用默认事务(mysql rr),因此造成了重复抢购的问题

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

应急解决方案

1、将finally中步骤4,删除key的操作去掉

这个方案虽然是解决了线上的问题,但是可能存在以下问题(我能想到的就这个)

1、如果用户操作一这个事务处理为11秒,这个时候操作二进来了,那么就又会存在重复抢购的问题。

2、线上存在大量redis 10秒后消失的key

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

其他解决方案:

也就是我们标题讲到的spring声明式事务@Transactional后置

后置方案一

1、在spring声明式事务@Transactional 方法do something()添加如下代码,在spring事务提交后再delete 对应的key

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

2、如果出现了异常,在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) {
    }
}

 

转载于:https://my.oschina.net/u/3081965/blog/1828800

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值