重复支付问题如何解决(悲观锁和乐观锁)
用户确认支付后,支付系统异步调用交易系统,交易系统更新交易状态,通知商家发货。如果交易系统超时未响应支付系统,支付系统会进行重试。有可能这时交易系统已经通知商家发货,这次的重试会让商家发货两次,这是不可以接受的。
这时,需要引入一个防重操作,例如,每次更新交易状态,先查询是否是初始状态,如果是,就更新为成功,并且通知商家发货。如果不是初始状态,就不通知商家发货。以下是伪代码演示:
1.select * from order_info where id = "20201020"
2.Java代码判断status == '初始' 执行3,否则返回
3.update order_info set status = '成功' where id = '20201020'并且发货
上面的防重操作并没有考虑并发的情况,当有两个请求都执行了1,都拿到初始状态,他们就都会去通知发货。
悲观锁方案
查询时加行锁。
begin transaction
1.select * from order_info where id = "20201020" for update 加record lock
2.Java代码判断status == '初始' 执行3,否则返回
3.update order_info set status = '成功' where id = '20201020'并且发货
commit
乐观锁方案
在update时加一个state = '初始'条件,如果state为初始,则影响行数为1,如果state为成功,则影响行数为0。通过最后一个更新操作的影响行数来判断是否返回。
begin transaction
1.select * from order_info where id = "20201020" for update 加record lock
2.Java代码判断status == '初始' 执行3,否则返回
3.update order_info set status = '成功' where id = '20201020' and state = '初始',判断影响行数,若为1则发货
commit