这是我参与更文挑战的第9天,活动详情查看: 更文挑战
一、什么是幂等性
幂等概念最初是来自数学领域,表示N次变换和1次变换的结果是相同的。这里讨论在某些场景下,客户端在调用服务没有达到预期结果时,会进行多次调用,为避免多次重复的调用对服务资源产生副作用,服务提供者会承诺满足幂等。
幂等性的实质就是当一次或多次请求同一个资源时,获得的结果是一致的。 比如查询数据库操作,没有增删改,因此没有对数据库有任何影响。
二、什么情况下需要幂等
就拿订单支付来说吧
- 前端调了一个创建订单的接口,第一次调用的时候超时了,然后调用方重试了一次
- 在订单创建时,我们需要去扣减库存,这时网络波动,调用没有生效,调用方重试了一次
- 在订单创建后支付动作时,服务端扣钱操作超时,调用方重试
- 订单状态更新操作中,连续发送了已创建和已支付的状态请求,先收到已支付后收到已创建
- 支付完成后的短信提醒,网络延迟,短信重复发送
这些场景都需要幂等性,防止重复请求或者多次调用导致数据不一致,结果不一致。
三、如何保证幂等性
唯一键
幂等需要通过唯一的业务单号来保证。相同的业务单号可以认为是同一笔业务,使用唯一标识的业务单号来确保对该业务单号的处理逻辑和执行结果都一致。
以创建订单为例,在不考虑并发的情况下,实现幂等:①先查询一下订单是否已经创建;②如果已经创建过,则返回创建成功;如果没有,就创建一条新的订单,并更新订单的状态。
唯一键可以是数据库表的主键或者由几个key值一起来确定唯一性。
加锁
乐观锁
乐观锁多用于查询数据上,在设计表结构时通过version版本来做乐观锁,这样既能保证执行效率,又能保证幂等,在查询数据时,先看version是否一致,一致则成功返回,然后version+1。
分布式锁
分布式锁就比如Redis,就已订单支付为例子,支付操作时会先查询redis中是否有订单号对应的key,如果没有,就新插入一条订单号的key,如果有的话直接进行支付操作,支付完成后删除该订单号的Key。采用Redis做的分布式锁,将key放在缓存中,这样查询效率更高。只有当一次支付完成后,才会进行下一次的操作,不会并行执行支付动作。
token
token和唯一键有点类似,只不过token是会被更新替换,并类似信物的存在。
它的过程是当用户创建订单时,会同时由系统根据用户信息缓存一个token值在redis中,并设置一个失效时间。
然后发起支付请求后,支付系统就会去redis中检查该token,如果存在就删除token开始支付操作;如果不存在就返回错误。
这里的token就是让支付系统确认用户的请求是否合法的标志。
防重复表
这个也是一样的道理,根据生成的订单号在去重复表中插入一条索引,当订单执行了支付操作时,无论执行的成功还是失败,都会将去重复表中订单号对应的数据进行更新为失败或成功,当再次有订单请求支付时,因为已经有唯一的索引,不能重复插入,所以就会操作失败。而知道仅有的第一次且唯一一次请求完成,结果可能是失败或成功。