REPEATABLE-READ隔离级别 事务中无法读到其它事务提交了的最新数据

前言:

业务要求一个简单的并发控制,使得一条数据只被确认一次,我的方案是 悲观锁,就是在事务中先对数据行加锁(MySQL InnoDB 行锁基于索引),判断是否已经确认过,未确认的情况下确认,已确认则事务提交释放锁。代码写完,结果发现未生效,就开始了满脑子问号的排查过程。

业务代码结构如下:

        //不要这么做 
        //一没对异常进行处理 
        //二事务的范围太大包含很多不需要在事务中的代码
        @Transactional
        public void dangerConfirm(ConfirmDangerRequest request) throws CommonException {
            String zhiXinBianHao = request.getZhiXinBianHao();
            //此处方法中包含一次查询操作  查询表 A数据
            CodeWxid codeWxid = hisDaoService.getCode(request.getConfirmUserId());
            if (codeWxid == null || codeWxid.getWxId() == null){
                throw new CommonException("工号或企业微信id有问题", ResultStatusCode.INVALID_CAPTCHA);
            }
            //在此行打断点先阻塞
            //悲观锁  锁表B中的一行数据  mybatis
            String lock = someMapper.lock("AED5ADC3C67E4A89AB7161DA84DC1FC1");
            System.out.println(lock);
            //用JPA查同一条数据 偷懒 表B
            BaseInfo baseInfo = baseInfoRepository.findByZhiXinBianHao("AED5ADC3C67E4A89AB7161DA84DC1FC1");
            //用mybatis查询同一行的某个字段  表B
            String te = someMapper.te("AED5ADC3C67E4A89AB7161DA84DC1FC1");

情况描述:

在MySQL命令行直接开事务,锁同一行,此时上文代码断点往下执行会等待锁的释放,正常。在命令行事务中更新数据中的某个字段,后提交。此时,调试代码获取到锁,向下执行时 发现问题:最后两行,均未查出命令行已经提交的字段的值,即 无法读到其它事务已经提交的数据。这和我所掌握的知识不符。

分析:

数据库MySQL的隔离级别时 RR,不会出现脏读和不可重复读。问题是现在其它事务提交的都读不到,但是数据库软件是可以查到的。没办法了,排除法,把所有与事务无关的注释掉,一执行,好了。。。。。。可以正常查到其它事务已提交的数据。

那么,自然地就定位到 下面这行的问题

//此处方法中包含一次查询操作  查询表 A数据
CodeWxid codeWxid = hisDaoService.getCode(request.getConfirmUserId());

加上上面这行,又不行了。。。。。除了悲观锁那行外,后面又读不到其它事务提交的数据了。。

发现了这个现象,下面就开始做实验(隔离级别为 REPEATABLE-READ):

起两个MySQL命令行客户端A、B,两边都 set autocommit = 0; start transaction;

实验一:A更新一条数据行 id = 1,值更新为 99,此时A不提交,B是查不到新值99的。A提交,B直接查询此条数据,可以查到值99。注意,在此之前B从未执行过查询操作。

实验一:A更新一条数据行 id = 1,值更新为 99,此时A不提交,B是查不到新值99的。A提交,B先任意执行一条查询,再查询此条id=1的数据,就不可以查到值99,查到的是之前的旧值。

这好像很符合 REPEATABLE-READ隔离级别的定义

使用 set @@session.tx_isolation='read-committed'; 将AB会话的隔离级别调整为 read-committed。发现:B事务任何情况下都可以读到A事务刚提交的最新数据。

总结:

MySQL默认的隔离级别为 REPEATABLE-READ,这个隔离级别使得 前后读取同一条的值是相同的,不会受其它事务的影响,除非它自己改变的。

 

 

 

 

 

 

你可能感兴趣的:(Java,Spring,spring,boot,事务,@Transactional)