幂等性设计

月伴飞鱼 2024-11-01 14:00:27
公众号文章
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!

重复请求

生产环境经常出现重复的数据,而这个绝大部分原因是发生了重复的请求。

重复请求是指同一个请求因为某些原因被多次提交。

导致这个情况会有几种场景:

微服务场景

  • 在微服务架构下,接口超时,微服务框架会进行重试。

用户交互的时候多次点击,如:快速点击按钮多次。

MQ消息中间件:消息重复消费。

第三方平台接口:因为异常也会导致多次异步回调。

幂等性定义

如上,针对重复请求,我们在设计某些接口时,要考虑如何保证接口幂等

那什么是接口幂等呢?

定义:多次调用对系统的产生的影响是一样的,即对资源的作用是一样的,但是返回值允许不同。

  • 并且不会因为多次点击而产生了副作用。

比如支付场景:

用户购买了商品支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了。

用户再次点击按钮,此时会进行第二次扣款,返回结果成功。

  • 如果用户查询余额返发现多扣钱了,流水记录也变成了两条,这就没有保证接口的幂等性。

幂等场景

查询

  • 不会对数据产生任何变化,具备幂等性。

新增

  • 数据都会新增多条,不具备幂等性。

修改

  • 直接赋值更新,具备幂等性。
  • 计算赋值更新,不具备幂等性。

删除

  • 多次操作,结果一样,具备幂等性。

总结:新增没有唯一主键约束的数据,和计算赋值更新操作都不具备幂等性。

直接赋值:

update user set num = 20 where userid=123;

计算赋值:

update user set num = num + 20 where userid=123;

幂等性方案

悲观锁

id字段一定是主键或者唯一索引,不然可能造成锁表的结果。

  • 数据锁定时间可能会很长,需要根据实际情况选用。
select * from user where id = 1 for update;

乐观锁

加上了版本号后,计算赋值更新场景,具备了幂等性。

缺点:

  • 在操作业务前,需要先查询出当前的version版本。
update user set  num= num + 20, version = version + 1 where userid=123 and version=1;

唯一约束

利用数据库的主键唯一约束的特性,解决在insert场景时幂等问题。

去重表

把唯一主键插入去重表,再进行业务操作,且他们在同一个事务中。

  • 这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。

去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。

  • 保证了数据一致性

去重表是跟业务无关的,很多业务可以共用同一个去重表。

  • 需要规划好唯一主键。

图片

分布式锁

如果多个机器可能在同一时间同时处理相同的数据:

  • 比如多台机器定时任务都拿到了相同数据处理。

就可以加分布式锁,锁定此数据,处理完成后释放锁。

  • 获取到锁的必须先判断这个数据是否被处理过。

Token机制

图片

服务端提供了发送token的接口。

在执行业务前,先去获取token,服务器会把token保存到redis中。

  • 然后调用业务接口请求时,把token携带过去。

服务器判断token是否存在redis中,存在表示第一次请求。

  • 这时把redis中的token删除,继续执行业务。

如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client

  • 这样就保证了业务代码,不被重复执行。

缺点

业务请求每次请求,都会有额外的请求:

  • 一次获取token请求、判断token是否存在。

真实的生产环境中,1万请求也许只会存在10个左右的请求会发生重试。

  • 为了这10个请求,需要让9990个请求都发生了额外的请求。

逻辑实现:

可以通过自定义注解将进行改造。

  • 在需要保证幂等的方法上,添加自定义注解即。

图片

总结

幂等性是系统服务对外一种承诺,特别业务中涉及的钱的部分,一定要慎重再慎重。

虽然前端做限制会更容易点,但前后端都需要做努力。

img
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!