Hefery 的个人网站

Hefery's Personal Website

Contact:hefery@126.com
  menu
73 文章
0 浏览
1 当前访客
ღゝ◡╹)ノ❤️

接口幂等性

什么是接口幂等性?

接口幂等性:用户对同一操作发起多次请求,对数据的影响是一致不变的,不会因为多次请求而产生副作用,即任意多次执行所产生的影响均与一次执行的影响相同

接口幂等性问题场景

支付场景:在订单的支付中,如果没有幂等性,接口的重试可能造成重复支付。用户购买商品后,发起支付操作,支付系统处理支付订单成功后,订单已经扣款成功,但是由于网络等原因没有及时返回给用户支付结果,相应的支付流水也都已经生成,这个时候用户又进行支付操作,进行第二次扣款,扣款成功后返回给用户。查看支付订单和流水会发现支付两次。

为什么要有幂等这种场景?

因为在大的系统中,都是分布式部署,如订单业务和库存业务都是独立部署的服务。用户下订单,会调用到订单服务和库存服务
比如:订单系统:订单服务 —> 库存服务 (PRC远程调用服务接口)
因为分布式部署,很有可能在调用库存服务时,因为网络等原因,订单服务调用失败,但其实库存服务已经处理完成,只是返回给订单服务处理结果时出现了异常。这个时候一般系统会作补偿方案,也就是订单服务再此放起库存服务的调用,库存减1:update t_goods set count = count -1 where good_id=2
这样就出现了问题,上一次调用已经减了1,只是订单服务没有收到处理结果。现在又调用一次,又减1,这样就不符合业务了。幂等是,不管库存服务在相同条件下调几次,处理结果都一样。这样保证补偿方案的可行性

问题场景:

  • 用户重复操作
  • 代码重试
  • 消息重复消费
  • 网络波动

幂等分析:单库单表

  • select 幂等:每次查询对数据都不会产生副作用
  • insert 不一定幂等:当我们重复插入数据的时候
    • 自增主键,无幂等性:insert into product_info (name,type,price)
    • 业务主键,具有幂等:insert into product_info (orderId,name,type,price) orderId主键唯一
  • delete 不一定幂等:当我们重复删除数据的时候
    • 绝对删除,具有幂等:delete from order where orderId = 3
    • 相对删除,无幂等性:delete from order where orderId > 23
  • update 不一定幂等:当我们重复更新数据的时候
    • 绝对更新,具有幂等:update good set stock= 586 where goodId = 10
    • 相对更新,无幂等性:update good set stock = stock+10 where goodid= 10

接口幂等性解决方案

  • 唯一索引去重

    防止新增脏数据

  • Token + Redis

    防止页面重复提交:由于重复点击或者网络重发或nginx重发等情况会导致数据被重复提交

    1. 获取全局唯一token:接口处理生成唯一标识(token) 存储到redis中,并返回给调用客户端

      后台校验token,同时删除token,生成新的token返回。redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用

    2. 发起支付操作并附带token

      • 获得分布式锁(处理并发情况:在高并发请求中 ,token判断是否存在是非线程安全)
      • 判断redis中是否存在token:存在-执行支付业务逻辑,否则-返回该订单已经支付
      • 释放分布式锁(处理并发情况:在高并发请求中 ,token判断是否存在是非线程安全)
  • 状态机

    电商订单支付状态: 0-待支付、1-支付中、3-支付成功、4-支付失败
    update order set status = 1 where status =0 and orderId = “201251487987”
    进行订单支付,上来先用CAS更新订单状态

    在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机,就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等

  • 乐观锁

    数据库乐观锁:update t_goods set count=count-1,version=version+1 where good_id=2 and version=1

  • 分布式锁

    如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁

  • 全局唯一ID


标题:接口幂等性
作者:Hefery
地址:http://hefery.icu/articles/2022/02/14/1644808249669.html