通过数据库的事务隔离级别来控制并发

问题复现:

     (1) 在service中,要生成主键,我们采取的策略是,先在主键工厂表中把当前最大主键查询出来,并让他加一,使这个值为新插入业务表数据的主键:

             TIdFactory idfactory= tIdFactoryDao.findById(map);
            
             Long id= KEYConstants.initial_id+idfactory.getIncrement()+1;

      (2) 封装业务数据,在业务表中进行数据插入

               User u= new User();

               u.setId(id);

               userDao.save(u);

        ( 3)  更新主键工厂表,让主键加一,供下次使用

              tIdFactoryDao.updateIncrement(id);

      说明: 上面1,2,3,在一个service方法中实现,service方法加上事务注解@Transactional;

        现在有两个controller同时调用该service的方法,当第一个controller在调用service方法运行到第2步的时候,第二个controller也调用了该service同时运行了第1步,那么当第二个controller运行第2步时,问题产生了,主键重复异常。

失败的解决方法:

        用synchronized锁把1,2,3锁在一个代码块中,使得在controller在执行1,2,3时,controller2是要等待的,本以为,事情得到解决,但是,由于整个service方法是有事务的,虽然加了锁,但是锁的级别是在spring的事务内,因此无法控制controller2去读取当前已经被controller1拿走的工厂内的当前的主键,也就是无法控制controller2去读取controller1已经读取过的id。

成功的解决方法:

         通过分析,要解决这个问题首先想到是把synchronized,放在controller层,这样锁的级别就大于了service的事务级别,但是我并没有这样做。

        分析了一下,当前我们使用的mysql数据库的事务隔离级别是可重复读

                                   

         也就意味着,这个数据库的隔离级别是可以控制脏读,和不可重复读的,那么,如果让步骤1的查询加上锁(带上事务),那么在controller1执行完步骤1,一直到当前service执行完事务被提交,这段时间,controller2是不能执行第一步的,它不能查询到当前这个在被更新的数据,因为它如果读取了数据,执行了第一步这就算脏读,这是mysql当前的隔离级别已经控制了的,所以controller2只能等待service事务提交,那么等到controller1的事务提交后,后面的步骤3自然也已经被提交,那么,controller2拿到的工厂中的id只能是最新的。自此,问题解决,解决方法就是 在步骤1的查询操作的sql上面加上for update。

关于mysql的事务隔离级别:https://blog.csdn.net/lcx390549721/article/details/81082361

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值