zoukankan      html  css  js  c++  java
  • 防重幂等

    前言:

    在分布式系统下,服务之间相互调用,必然会存在调用失败并且进行重试的情况,在某些情况下就需要做好防重幂等。

    防重和幂等是什么?

    防重:避免产生重复数据

    幂等:除了避免产生重复数据之外,还要求每次请求都返回一样的结果

    什么情况会导致重复?

    发送方发送相同的请求到服务端。

    • 前端多次发送相同的请求到后端
    • 超时重发导致的重复
    • MQ异常导致的重复消费

    如何防重?

    • insert之前先select,通常情况下有效,但是在高并发情况下,也会导致重复

    • 建立唯一索引,数据库兜底,防止重复添加

    • 某些业务表在特定的场景下才不允许重复,不能直接建立唯一键,就可以增加一张防重表(为此类业务),将此类数据在同一事务下先insert进防重表成功,在insert业务表,假如insert进防重表失败,证明此类数据重复,就不用再处理业务表了

    • 加分布式锁(针对单据来锁):需要合理设置过期时间。不能太短,导致业务没有处理完,锁失效,防重失败;也不能不设置过期时间,解锁异常导致锁一直被占,阻塞后续处理。

    什么情况要做幂等?

    用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。

    例如:

    • 比如用户对一笔订单发起付款,因为网络问题没有返回结果,就多次点击付款按钮,此时只能发起一笔真实的交易,生成一条交易记录。
    • 分布式系统中,因为接口超时,导致的重试,第一次请求接口超时,没有获取到返回结果(有可能已经成功了),第二次重试,接收方不能直接返回失败,要根据第一次处理的结果进行返回。

    怎么解决?

    1. 新增数据类接口,通过防重解决。
    2. 更新类接口,比如更新库存,更改状态等,通过状态,加乐观锁解决。

    根据状态判断

    很多业务是有状态的,比如一个订单表。有下单0、支付中1、已支付2、取消支付3等状态,

    假如id=123的订单状态是0,现在要变成支付中状态。

    update order set status=1 where id=123 and status=0;

    第一次请求时,该订单的状态可以正常更新,sql执行结果的影响行数是1,订单状态变成了1。后面有相同的请求过来,再执行相同的sql时,由于订单状态变成了1,再用status=0作为条件,最终sql执行结果的影响行数是0,即不会真正的更新数据。但为了保证接口幂等性,接口也需要直接返回成功。

    加乐观锁,在表中增加一个version字段。

    在更新数据之前先查询一下数据:

    select id,amount,version from user id=123;

    如果数据存在,假设查到的version等于1,再使用id和version字段作为查询条件更新数据:

    update user set amount=amount+100,version=version+1 where id=123 and version=1;

    更新数据的同时version+1,然后判断本次update操作的影响行数,如果大于0,则说明本次更新成功,如果等于0,则说明本次更新没有让数据变更。

    由于第一次请求version等于1是可以成功的,操作成功后version变成2了。这时如果并发的请求过来,再执行相同的sql:

    update user set amount=amount+100,version=version+1 where id=123 and version=1;

    该update操作不会真正更新数据,最终sql的执行结果影响行数是0,因为version已经变成2了,为了保证接口幂等性,接口可以直接返回成功,因为version值已经修改了,那么前面必定已经成功过一次,后面都是重复的请求。

    总结

    • 网络延迟问题:先发的不一定先到
    • 数据库操作延迟:先到的不一定先执行完
    • 不能依赖上游或下游去做防重幂等,自己本身也要把控好
    • 对于外部接口,没有明确返回可重试状态的,不要轻易重试
  • 相关阅读:
    数组
    字符对象的方法
    事件
    判断数据类型
    数据类型和变量
    语法
    快速入门
    JavaScript简介
    Spring init-method和destroy-method属性的使用
    spring3后提供了的context:property-placeholder/元素
  • 原文地址:https://www.cnblogs.com/jiezai/p/15499774.html
Copyright © 2011-2022 走看看