幂等性校验问题请教

来源:7-8 支付过程

慕用6421140

2019-01-23

图片描述老师您好,有个问题请教一下,有没有可能T1和T2。两个请求同时执行到52 这一行,如果可以的话,感觉幂等性校验没起作用,重复支付没有识别出来,如果payInfo表有唯一索引会如何呢?

写回答

2回答

大漠风

2019-01-23

这个问题非常好,能够在平常都能注意到每个方法会不会有并发问题。有了这样的意识,是做好分布式系统的关键。在考虑并发的时候,我们要考虑并发操作同一个数据的问题。那么这个“同一个数据”肯定是根据一定条件来得到这一个数据的。例如一个人同时买多个商品,那么用户的余额就是要操作的同一条数据。

但是,在这节的这里实例当中,这个方法是由消息触发的,也就是:

@Transactional
@JmsListener(destination = "order:pay", containerFactory = "msgFactory")
public void handle(OrderDTO msg) {
    LOG.info("Get new order to pay:{}", msg);
    // 先检查payInfo判断重复消息。
    PayInfo pay = payInfoRepository.findOneByOrderId(msg.getId());
    if (pay != null) {
        LOG.warn("Order already paid, duplicated message.");
        return;
    }
    。。。

所以,对于同一个订单,这个方法不会被并行触发多次,只有失败了一次以后,再被消息重试触发。而这个支付信息,是由这个订单生产的支付信息,就不会出现对同一个订单,保存多个payInfo的情况。如果是对于不同订单,这边即使并发执行到这里,也不会有影响。

在这个方法里,在视频中我强调了针对扣费的操作,没有使用:

        customer.setDeposit(customer.getDeposit() - msg.getAmount());
        customerRepository.save(customer);

就是怕同一个用户,几乎同时下了多个订单,那么先获取用户信息,再更新余额,再保存的方式就会出现问题,后面保存的方法会把前面的覆盖。然后,下面的同学也提到数据库的隔离级别,但是对于

customerRepository.save(customer)

这个操作,数据库的隔离级别保证读到的是另一个请求还未提交的,也就是未修改的余额值。update的时候,就是update这条记录,这时候即使另一个请求已经提交,也会按原先的余额扣除后保存。

0
3
慕用6421140
回复
大漠风
好的,感谢老师!
2019-01-23
共3条回复

慕用6421140

提问者

2019-01-23

根据mysql的默认隔离级别,应该不会锁住读请求吧

0
3
大漠风
回复
小动物很困
对,你读出来的数据可能已经被另一个并发的请求修改了。
2019-03-28
共3条回复

分布式事务实践,从原理到实例,解决数据一致性

掌握分布式事务实现技术,是架构师必备技能。

1149 学习 · 153 问题

查看课程