mysql悲观锁和乐观锁生产实践

场景描述

最近,生产系统遇到了一个mysql数据更新的并发问题,业务场景是这样的:
在这里插入图片描述

  1. WMS每次退货商品入库后,通知支付系统退款,支付系统退款成功后,向退款事件表中插入退款信息
  2. 退款处理作业轮询退款成功事件,获取待处理的任务
  3. 根据退货单号,查询已退款信息,并将支付事件中的金额与已退金额相加,进行更新,如果已退金额达到了退货申请金额,则更新退货单状态为“已完成”。

生产问题

支付系统推送统一退货单的两次退款事件的时间非常接近,几乎同时插入到事件表中,导致作业在执行时将两次时间也几乎在同一时间进行处理,而数据库此时并没有加锁,两次作业读取的已退金额都是0,然后进行相加计算后,更新数据库记录时就导致后更新的数据覆盖了先更新的记录,导致数据丢失,退货单状态无法变更到“已完成”。
在这里插入图片描述
如上图示意,最终正确结果本应该是refund_amount = 30 ,但是因为被覆盖了,所以最终会丢失一次入库的数据。

解决方法

数据库进行更新时对该记录进行加锁。

1 悲观锁

每次执行查询时,通过for update来对需要更改的记录进行锁定,这样下次更改只能等待第一次修改完成后才能进行第二次读取,因此计算出的结果可以保证正确性。具体实现:将原来的语句

	 select * from refund_order where id=#{id} 

更改为

	 select * from refund_order where id=#{id} for update

因为当系统的流量其实并不高,为了快速解决此问题,暂时采用这种快速修复的方法,当然也可以考虑用乐观锁进行更新。

2 乐观锁

乐观锁就是在进行更新是,对当期读取到的值与数据库值进行比较,例如更新前读取数据库的值为0,更新时也需要加上where条件refund_amount=0, 这样如果当前更新操作被其他线程抢先执行了,则会更新失败,然后通过作业重新执行来避免并发问题。乐观锁适合并发发生情况比较少的情况,因为 如果一直更新失败,效率反而不如使用悲观锁高。还有要注意的问题是当前的场景下因为都是整数更新,所以不会产生aba的问题,如果有负数更新, 则需要考虑aba问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值