什么是幂等性
-
幂等性定义:
- 一次和屡次申请某一个资源对于资源自身应该具备同样的后果
- 任意屡次执行对资源自身所产生的影响均与一次执行的影响雷同
-
幂等性定义的几个重点:
-
幂等不仅仅只是一次或者屡次申请对资源没有副作用
- 比方, 查询数据库操作, 没有增删改, 无论多少次操作对数据库都没有任何影响
- 幂等还包含第一次申请的时候对资源产生了副作用, 然而当前的屡次申请都不会再对资源产生副作用
- 幂等关注的是当前屡次申请是否对资源产生副作用, 并不关注后果
- 网络超时等问题, 不是幂等的探讨范畴
-
- 幂等性是零碎服务对外一种承诺, 而不是实现
- 承诺只有调用接口胜利, 内部屡次调用对系统的影响是统一的
-
申明为幂等的服务会认为内部调用失败是常态, 并且失败后必然会有重试
幂等性的应用场景
-
业务开发中, 常常遇到反复提交的状况:
- 因为网络问题无奈收到申请后果而从新发动申请
- 前端的操作抖动而造成的反复提交的状况
-
在交易系统中, 领取零碎这种反复提交造成的问题尤为显著:
- 用户在 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 tab1 SET col1=1,version=version+1 WHERE 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 开始领取逻辑解决
-
如果缓存中不存在, 示意非法申请
领取缓冲区
-
-
领取缓冲区:
- 将订单的领取申请都疾速地接管下来, 是一个疾速接管申请的缓冲管道
- 应用 异步 工作解决管道中的数据, 过滤调掉反复的待领取的数据
- 长处: 同步转异步, 高吞吐
-
毛病: 无奈及时返回领取后果, 须要后续监听领取后果的异步返回
- 幂等是为了简化客户端逻辑, 然而减少了服务提供者的逻辑和老本
- 幂等的应用须要依据具体场景具体分析
- 减少了额定管制幂等的业务逻辑, 简单了业务性能
-
将并行的性能转化为串行, 升高了执行效率