关于mysql事务更新可读性问题

最近在工作中遇到了一个事务之间可读性的问题,业务场景是这样的:

    用户创建了订单之后会在后台创建一个延时15分钟的任务,当15分钟到的时候会检测这个订单是否支付了,如果支付了那么就会取消这个延时任务,如果没有支付那么就取消这个订单,但是现在有用户反馈已经支付了但是订单还是取消了,于是就这个问题我到代码里找了一下,发现了这个可读性的问题。代码逻辑是这样的

@Transactional(value = "gaotuTransactionManager", readOnly = false, rollbackFor = RuntimeException.class)
    public int cancelOrder(Long payNumber) {
        //1、获取订单信息
        //2、检查是否是未支付
        //3、更新payInfo
        
        return 1;
    }

在这里更新payInfo使用的sql是

update order set status = 1 where payNumber = 123123

这样这里就会出现一个问题,就是当上面1、2步走完的时候恰好用户支付了订单,但是因为在2检查的时候是未支付的所以3还是会把这个订单取消掉,所以这里的sql这样写是有问题的,改成下面这样就可以了。

update order set status = 1 where payNumber = 123123 and status = 2

但是这样是不是真的就没有问题了呢,于是我到测试环境去验证了一下,结果如下:

首先我开了两个mysql链接并开启事务分别为A和B,A先开启事务但是B先结束事务:

A1: start transaction;
         B1: start transaction;
A2: select * from order where payNumber = 1;
         B2: update order set status = 1 where payNumber = 1;
A3: select * from order where payNumber = 1;
         B3: commit;
A4: select * from order where payNumber = 1;

这个时候我发现三次select请求出来的结果重的status都是2也就是说尽管B3已经更新了但是A4还是读取的原来的数据,原因是mysql的innodb 默认的隔离级别是RR也就是可重复读,可重复读的情况下,某个事务首次read记录的时间为T,未来不会读取到T时间之后已提交事务写入的记录,以保证连续相同的read读到相同的结果集

如果这个时候把隔离级别换成是RC也就是读提交呢?结果会是什么样?

结果就是A1:2,A2:2,A3:2,A4:1,在A4的时候读取到了事务B提交的新的数据。

关于这个RR和RC为啥出现这种情况是因为mysql的innodb使用了快照读这个功能,什么是快照读呢?

MySQL数据库,InnoDB存储引擎,为了提高并发,使用MVCC机制,在并发事务时,通过读取数据行的历史数据版本,不加锁,来提高并发的一种不加锁一致性读(Consistent Nonlocking Read)。

读提交(RC),可重复读(RR)两个不同的事务的隔离级别下,快照读有什么不同呢?

  • RC下,快照读总是能读到最新的行数据快照,当然,必须是已提交事务写入的

  • RR下,某个事务首次read记录的时间为T,未来不会读取到T时间之后已提交事务写入的记录,以保证连续相同的read读到相同的结果集

到目前算是解决的为什么订单状态异常的问题,果然上线之后用户类似的反馈没了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值