用户确认支付后,支付系统异步调用交易系统,交易系统更新交易状态,通知商家发货。如果交易系统超时未响应支付系统,支付系统会进行重试。有可能这时交易系统已经通知商家发货,这次的重试会让商家发货两次,这是不可以接受的。
image

这时,需要引入一个防重操作,例如,每次更新交易状态,先查询是否是初始状态,如果是,就更新为成功,并且通知商家发货。如果不是初始状态,就不通知商家发货。以下是伪代码演示:

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