记一次不同应用并发处理,利用mysql事务和行锁

利用mysql特性

隔离级别

首先数据库是5.6.70,默认隔离级别为可重复读(Repeatable Read) 数据库隔离级别 可重复读 理解
事务开始后直到结束,读取的永远都是同一个值。

行锁

update默认有行锁 mysql update 锁行还是锁表

项目需求背景

原先签到活动 奖品实例和优惠券实例 生成消费逻辑

  • 原先在活动审核成功后 奖品实例和优惠券实例 由后台生成
  • 用户签到请求接口这边 对奖品实例和优惠券实例进行绑定,库存减一,使用的是先查询,在代码上减一,再去update

现需求 为签到活动追加奖品实例和优惠券实例

  • 后台生成的时候,接口这边就进行消费,刚生成的优惠券,就被绑定了,这是不好的,可能存在未知问题,不过因为事务的存在,直到事务结束,生成的时候都不会被读取到,所以不用担心
  • 库存并发问题:后台生成完实例后,对原库存进行增加,但是接口也可能进行减库存

解决办法

方法一:不可取

两边都加redis锁 ,分别增加,最后在更新,由于锁的存在,只有一条语句在操作此库存

下面是伪代码

后台事务A{

​	redis锁A{
​			select stock;
			stock=stock+N;
​			update  stock=xx where id=1;
​	}

}



接口事务B{

​	redis锁A{
​			select stock;
			stock=stock-1;
​			update  stock=xx where id=1;
​	}

}

想得很好,但是仔细再想想却发现有漏洞

  1. 后台事务A 的 redis锁A 执行完瞬间,事务还未提交
  2. 接口事务B的redis锁A开始执行,这时候查询出的库存,并不是 后台事务A 更新后最新的
  3. 这时候 后台事务A 也执行完了,库存更改了
  4. 接口事务B 执行到 update,就会把后台事务A更新的库存给覆盖掉,就会导致数据脏掉

方法二:可取

利用上面讲的mysql update行锁特性便可以,同样是使用update,却不用在代码里查询出来在增删
后台 直接加,由于id为索引会锁行,而不是锁表

UPDATE stockTable a SET a.currentInventory = a.currentInventory + N WHERE a.id = 1

接口,判断库存大于0,直接减1,代码这边根据更新的行数,判断是否领取成功即可

UPDATE stockTable a SET a.currentInventory = a.currentInventory - 1 WHERE a.id = 1 AND a.currentInventory > 0

这样子非常方便简单,连分布式锁都不用加。

猜你喜欢

转载自blog.csdn.net/Fire_Sky_Ho/article/details/120229922