spring事务方法的同步问题

一、事务方法的同步问题

例如以下这段代码

    @RequestMapping("/test")
    @Transactional
    public int test(){
        Test test = testMapper.selectById(1);
        int max = test.getMax() + 1;
        test.setMax(max);
        testMapper.updateById(test);
        return max;
    }

当用jmeter并发访问1000次这个接口,发现max的值为532,跟预期的1000不一样。

这是因为有些请求查询不到数据库最新的值,导致的同步问题。

 

这里我使用的项目是springboot+mybatisplus+mysql

 

 

二、解决方法

1.使用select for update解决

     @Select("select * from test where id = #{id} for update")
     Test selectForUpdate(@Param("id") long id);

for update是一种行级锁,又叫排它锁,一旦用户对某个行施加了行级加锁,则该用户可以查询也可以更新被加锁的数据行,其它用户只能查询但不能更新被加锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。

只有当出现如下之一的条件,才会释放共享更新锁:
1、执行提交(COMMIT)语句
2、退出数据库(LOG OFF)
3、程序停止运行

 

 

2.使用手动事务+同步锁synchronized

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;


    @RequestMapping("/test")
    public synchronized int test(){
        int max = 0;
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try{
            Test test = testMapper.selectById(1);
            max = test.getMax() + 1;
            test.setMax(max);
            testMapper.updateById(test);
            platformTransactionManager.commit(transactionStatus);
        }catch (Exception e){
            platformTransactionManager.rollback(transactionStatus);
        }
        return max;
    }

这里使用手动事务的原因是,spring aop中,在方法执行前开启事务,方法执行后提交事务,也就是说事务的开启和提交过程是没有加锁的,因此无法实现同步。

 

 

以下方式是无法实现同步的:

    @RequestMapping("/test")
    @Transactional
    public synchronized int test(){
        Test test = testMapper.selectById(1);
        int max = test.getMax() + 1;
        test.setMax(max);
        testMapper.updateById(test);
        return max;
    }

将数据库值清0,再次使用jmeter测试,发现max的值为647。

说明依然没有解决同步问题。

 

就算把synchronized放到方法体里也是一样无法实现同步的,锁住的只是方法里的代码块,没有锁住事务的开启和提交过程。

    private Object object = new Object();
    
    @RequestMapping("/test")
    @Transactional
    public int test(){
        int max = 0;
        synchronized (object) {
            Test test = testMapper.selectById(1);
            max = test.getMax() + 1;
            test.setMax(max);
            testMapper.updateById(test);
        }
        return max;
    }

 

 

 

3.使用乐观锁

数据库添加version字段。

public class Test {
    private Long id;
    private Integer max;
    @Version
    private Integer version;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getMax() {
        return max;
    }

    public void setMax(Integer max) {
        this.max = max;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }
}

 

    @RequestMapping("/test")
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public int test(){
        int i = 0;
        Test test = null;
        while(i<1){
            test = testMapper.selectById(1);
            test.setMax(test.getMax()+1);
            i = testMapper.updateById(test);
        }

        System.out.println(test.getMax());
        return test.getMax();
    }

这里因为使用了mybatisplus框架,只需要在bean类version属性添加@Version注释,然后使用框架提供的方法查询和修改,默认支持乐观锁。

需要注意的是,这里@Transactional注释中标明了隔离级别为读取已提交。

否则会导致在事务中查询不到数据库最新的数据,导致一直无法更新成功,一直回旋。

原因是@Transactional默认的隔离级别是根据数据库的隔离级别,由于我用的是mysql的innodb引擎,默认的隔离级别是支持可重复读的,导致在事务中,查询数据库的值都是一致的可重复读的,导致无法获取数据库最新的值。

因此将事务隔离级别改为读取已提交就可以解决

 

 

三、总结

事务方法的同步可以使用以下方法解决:

select for update

手动事务+同步锁synchronized(可将锁细化)

③乐观锁

 

并发量不是特别高的话,使用乐观锁的效率最高。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring事务和锁冲突失效问题是在并发环境下常见的挑战之一,可以采用以下方法来解决这个问题。 首先,对于Spring事务冲突失效问题,可以考虑以下策略: 1. 调整事务隔离级别:通过提高事务的隔离级别,如将隔离级别设置为Serializable,可以避免脏读、不可重复读和幻读等并发访问问题。 2. 使用乐观锁:在进行并发操作时,通过使用版本号或时间戳等机制来对数据进行控制,从而避免冲突。 3. 使用悲观锁:在进行并发操作时,通过对数据进行加锁,限制其他事务对数据的访问,避免冲突。 4. 使用分布式锁:在分布式环境下,通过使用分布式锁来控制并发访问,避免冲突。 其次,对于锁冲突失效问题,可以采取以下方法: 1. 减小锁粒度:将锁应用到最小的代码块,避免锁住不需要同步的代码部分,从而减少锁冲突的可能性。 2. 使用更合适的锁策略:在并发情况下,选择合适的锁策略,如公平锁、非公平锁、读写锁等,以提高并发访问效率。 3. 使用分布式锁:在分布式环境下,使用分布式锁来对资源进行同步处理,避免锁冲突失效的问题。 4. 优化系统设计:通过优化系统架构和设计,尽可能减少并发访问的需求,从而降低锁冲突的可能性。 总之,解决Spring事务和锁冲突失效问题需要综合考虑事务隔离级别、锁策略、锁粒度和系统设计等多个因素。通过合理选择和调整这些策略,可以有效地缓解并发环境下的问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值