什么是幂等性

  • 幂等性定义:

    • 一次和屡次申请某一个资源对于资源自身应该具备同样的后果
    • 任意屡次执行对资源自身所产生的影响均与一次执行的影响雷同
  • 幂等性定义的几个重点:

    • 幂等不仅仅只是一次或者屡次申请对资源没有副作用

      • 比方,查询数据库操作,没有增删改,无论多少次操作对数据库都没有任何影响
    • 幂等还包含第一次申请的时候对资源产生了副作用,然而当前的屡次申请都不会再对资源产生副作用
    • 幂等关注的是当前屡次申请是否对资源产生副作用,并不关注后果
    • 网络超时等问题,不是幂等的探讨范畴
  • 幂等性是零碎服务对外一种承诺,而不是实现
  • 承诺只有调用接口胜利,内部屡次调用对系统的影响是统一的
  • 申明为幂等的服务会认为内部调用失败是常态,并且失败后必然会有重试

    幂等性的应用场景

  • 业务开发中,常常遇到反复提交的状况:

    • 因为网络问题无奈收到申请后果而从新发动申请
    • 前端的操作抖动而造成的反复提交的状况
  • 在交易系统中,领取零碎这种反复提交造成的问题尤为显著:

    • 用户在APP上间断点击屡次提交订单,后盾应该只产生一个订单
    • 向领取零碎发动申请,因为网络问题或者零碎Bug问题导致重发,领取零碎应该只做一次扣除操作
  • 申明幂等的服务认为,内部调用者会存在屡次调用的状况,为了避免内部屡次调用对系统的数据状态产生屡次扭转,须要将服务设计为幂等

    幂等和防重

  • 反复提交的状况和服务幂等的初衷是不同的

    • 反复提交是在第一次申请曾经胜利的状况下 ,人为地进行屡次操作, 导致不满足幂等要求的服务屡次扭转状态
    • 幂等更多应用的状况是第一次申请因为某些状况,不如超时,而导致不晓得后果或者申请失败的异常情况下,发动屡次申请
  • 幂等的目标是申请屡次确认第一次申请胜利,不会因为屡次申请而呈现屡次的状态变动

    保障幂等性的状况

  • 在SQL中,有以下三种场景,只有第三种场景须要保障幂等性:

    • SELECT col1 FROM tab1 WHERE col2=2 : 无论执行多少次都不会扭转状态,是人造的幂等
    • UPDATE tab1 SET col1=1 WHERE col2=2 : 无论执行胜利多少次状态都是统一的,也是幂等操作
    • UPDATE tab1 SET col1=col1+1 WHERE col2=2: 每次执行的后果都会发生变化,这种不是幂等的,要采取策略保障幂等性

      设计幂等性服务

  • 幂等使得客户端逻辑解决很简略,然而服务端逻辑会很简单
  • 满足幂等性服务须要蕴含两点逻辑:

    • 首先去查问上一次的执行状态,如果没有则认为是第一次申请
    • 在服务扭转状态的业务逻辑前保障防反复提交的逻辑

      保障幂等策略

  • 幂等须要通过惟一的业务单号来保障:

    • 雷同的业务单号,认为是同一业务
    • 应用惟一的业务单号确保:前面屡次雷同业务单号的解决逻辑和执行成果是统一的
  • 幂等实现示例-领取:

    • 先查问订单是否领取过
    • 如果曾经领取过,返回领取胜利
    • 如果没有领取,则进行领取流程,批改订单的状态为已领取

      防反复提交策略

  • 在保障幂等的策略中,执行是分两步执行的,前面一步依赖下面一步的查问后果,这样就无奈保障原子性
  • 无奈保障原子性在高并发的状况下会存在问题:

    • 第二次申请在第一次申请的下一步订单状态没有批改为"已领取状态"时进行
    • 为了解决这个问题 :将查问和变更状态操作加锁,并将并行操作改为串行执行

      乐观锁
  • 如果只是更新已有的数据,没有必要对业务进行加锁
  • 设计表构造时应用乐观锁,个别通过version来实现乐观锁:

    • 保障执行效率
    • 保障幂等

      UPDATE tab1SET    col1=1,version=version+1WHERE    version=#version# 

      因为ABA问题会导致乐观锁存在生效的状况,只有保障version值自增就不会呈现ABA的问题

      防重表
  • 应用orderNo作为去重表中的惟一索引,每次申请都依据订单号orderNo向去重表中插入一条数据:

    • 第一次申请查问订单领取状态:

      • 订单没有领取
      • 进行领取操作
      • 无论胜利与否,执行实现之后更新订单的状态为胜利或失败,删除去重表中的数据
      • 后续订单因为表中的惟一索引插入失败,返回操作失败,直到第一次申请实现(胜利或者失败)
  • 防重表的作用是实现加锁的性能

    分布式锁
  • 能够应用Redis分布式锁代替防重表的性能
  • 示例:

    • 订单发动领取申请
    • 领取零碎会去Redis缓存中查问是否存在该订单Key
    • 如果不存在,向Redis中减少Key为订单号
    • 查问订单领取是否曾经领取
    • 如果没有则进行领取,领取实现后删除该订单的Key
  • 通过Redis实现分布式锁,只有这次订单申请实现,下次申请才会进来
  • 比照去重表,Redis分布式锁将放并发做在缓存中,效率更高
  • 同一时间只能实现一次领取申请

    token令牌
  • token令牌分为两个阶段:

    • 申请token阶段:

      • 在进入到提交订单页面之前,须要订单零碎依据用户信息向领取零碎发动一次申请token的申请
      • 领取零碎将token保留到Redis缓存中,给领取阶段应用
    • 领取阶段:

      • 订单零碎获取到申请的token, 发动领取申请,
      • 领取零碎查看Redis是否存在该token

        • 如果存在,示意第一次发动领取申请,删除缓存中的token开始领取逻辑解决
        • 如果缓存中不存在,示意非法申请

          领取缓冲区
  • 领取缓冲区:

    • 将订单的领取申请都疾速地接管下来,是一个疾速接管申请的缓冲管道
    • 应用异步工作解决管道中的数据,过滤调掉反复的待领取的数据
  • 长处: 同步转异步,高吞吐
  • 毛病: 无奈及时返回领取后果,须要后续监听领取后果的异步返回

  • 幂等是为了简化客户端逻辑,然而减少了服务提供者的逻辑和老本
  • 幂等的应用须要依据具体场景具体分析
  • 减少了额定管制幂等的业务逻辑,简单了业务性能
  • 将并行的性能转化为串行,升高了执行效率