SpringBoot的事务与锁

在一人一单问题里,为什么加了事务还是会出现一人下多单呢?
本质的原因是,我们使用Java的对象锁,可以保证临界区只有一个线程访问,但是这和SpringBoot里加@Transactional注解不是等价的。数据库里的事务保证的是要么全部完成要么全部不起作用。在开启事务的时候,会有不同的数据库锁保证并发性。但是并不能保证我们执行逻辑的正确性,见下面的例子。

@Transactional //事务 涉及两张表,需要事务
public void createVoucherOrder(VoucherOrder voucherOrder) {
    // 一人一单,检查用户是否下过单
    Long userId = voucherOrder.getUserId();
    int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder).count();
    if (count > 0) {
        log.error("用户已经购买过");
        return;
    }
    // 扣减库存
    boolean success = seckillVoucherService.update()
            .setSql("stock=stock-1")
            .eq("voucher_id", voucherOrder.getVoucherId())
            .gt("stock", 0)
            .update();
    if (!success) {
        log.error("库存不足");
        return;
    }
    save(voucherOrder);
}

开启事务以后,对于第一个查是否下过单的操作,由于是快照读没有加锁,多个线程读到都是满足下单条件的。
第二步update就是当前读了,走了主键索引,会触发行级锁。
在这里插入图片描述

可以看出,加锁的类型是X型的记录锁,对那一行加了锁。
然后有第二个线程要执行同样的操作是会阻塞,但是不会下单失败。等第一个线程提交了,第二个线程也会提交。所以不能够解决一人一单的问题。
所以本质就是第一个操作没加锁,除非直接加表级锁,但是效率太低了。

所以可以用Java的对象锁来解决这个问题,但是不是简单的加sychronized就行了,可以详见这篇。为什么加了锁还有事务问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值