如何使用乐观锁防止数据冲突

如何使用乐观锁防止数据冲突

并发情况下的数据冲突

mysql如果事物隔离级别为可重复读时,会出现以下问题。

假如商家A(id位1000)的某个商品受到了欢迎,被人疯狂下单,这时候商家A的钱应该是疯狂暴涨的。那A的钱应该怎么增加呢?

select balance from account where id=1000;
update account set balance=balance+price where id=1000;

特殊注明:所有的sql都只是为了表达意思,无法直接使用。

这个逻辑大概就是这样的,先查出来余额,然后将余额+价格存进去。

但是有一个问题,如果两个顾客同时下单会怎么样?

当查询完毕之后,比如两个事物查询到的结果都是10000,然后价格是500,这时候第一个赋值完成了10500,第二个赋值的时候依旧是10500。

这时候商家A不干了,你们买了两件商品,给我一份的钱。顾客也不干了,是是是,没给你加上,又不是没给我扣钱。凭空蒸发500元。

加锁?枷锁?

select balance from account where id=1000 for update;
update account set balance=balance+price where id=1000;

这相当于直接加了行锁,我访问的时候谁也别访问。

但是有个问题,如果高并发情况下,直接无脑加锁有点难搞啊。尤其是没有竞争的情况下,这不妥妥自己给自己上枷锁吗?

再举个例子,你这边加行锁了,这时候另一个请求仅仅想看看账户余额,比如商家A看看自己赚了多少钱,并不去修改,完了,废了,等着竞争锁吧。

乐观锁行吗?

乐观锁确实比悲观锁要好一点,但是实现起来可能需要费点劲。

首先增加一个version(版本号)字段,接下来select的时候获取一下版本号。

第二步,更新的时候,判断条件的时候,除了判断id,还要判断版本号是不是一样。

如果一样就更新,并且更新余额的时候把版本号+1.

如果不一样,那么就触发重试。

select balance,version from account where id=1000;
update account set balance=balance+price,set version=version+1 where id=1000 and version=version;

注意事项

可以使用spring-retry进行重试,如果更新不成功就去重试,几次之后还不成功就返回异常,让用户等会再试。避免频繁的重试引起更大的资源浪费。

update更新失败是通过返回值为0,即更新了0行让业务层感知到的,而引起update更新了0行,不一定就肯定是并发冲突啊,所以需要做好相应的判断。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值