敲黑板,竟然还不知道幂等是怎么回事?

新年好啊,各位朋友!今天是年后开工的第一天,不知道你们有和我一样今天敬业的在岗吗?美好的时光总是短暂的,不知不觉史上最长春节假期就这样过去了,真是心痛!

唉,多的不谈,回归今天的正题,聊聊和有关幂等方面的东西。

什么是幂等?

幂等其实是一个数学与计算机学的概念,在抽象代数中比较常见。那么在编程中,对于一个幂等操作,它主要表现为多次执行结果与单次执行结果一致,即同样的参数以及条件,无论执行多少次,最终的结果都是一致的。

当然,幂等也可以分为请求幂等和业务幂等两类:

  • 请求幂等:相同参数多次请求,结果应当保持一致。
  • 业务幂等:同一个业务流程,在推进到终态之后的每次请求,响应结果都应当是一致的。在此之前,每次请求都需要执行相关的业务逻辑,直到进入终态。

对于开发人员来说,保证幂等性是在日常开发过程中是必须要考虑到的。像一些转账、下单支付这类涉及金钱交易的场景,必须要保证其幂等性,否则后果是相当严重的。

如何保证幂等?

讲了上面这些,那么对于开发人员来说,在日常的开发过程中如何来保证幂等性呢?

  • 数据库唯一索引
    一般我们在进行落库新增操作时,通常会先去查这个数据记录在不在库里,不存在就落库,存在就不执行。但是对于并发较高的场景时,如果存在多个请求同时去查库,并且都没有查到记录。那么后续的话就会去执行落库新增操作,也就会产生相同的重复记录,这显然不符合我们的需求。通过使用数据库唯一索引的方式,当前一个请求插入成功后,后续请求由于索引的唯一性,再次执行就会报冲突,这也能够保证幂等性。(一定要有!)
  • 状态机
    状态机对于很多开发人员来说可能比较陌生,其作用就是为了业务状态正常的流转。例如用户下了一笔订单,此时订单状态为已下单,若成功支付,则流转至已支付待发货;若超时未支付或者手动取消,则流转至超时关闭或者已取消。每次在执行更新操作时,先拿到当前的状态进行对比,符合条件则继续更新流转。
  • 加锁(乐观锁、悲观锁或者分布式锁)
    先说悲观锁,依靠数据库层面提供的锁机制,保证操作最大程度的独占性。在整个数据处理的过程中,数据一直处于锁定状态,防止其它请求更改该数据。并且悲观锁需要在同一事务中锁住一行数据,如果事务比较长,会造成大量的请求处于等待状态,影响接口性能。再说乐观锁,其主要是通过数据版本记录来实现的,即增加版本号。每次请求之前将版本号先拿出来,后续执行更新操作时,再对该版本号进行增加。携带的版本号若与数据库中记录的版本一致,则执行更新操作,后续请求再执行的话,版本号不一致则不会再去更新数据记录。
    再来说说分布式锁,常见于使用Redis或者Zookeeper实现分布式锁,每次请求通过业务唯一ID尝试获取分布式锁,拿到锁了之后,执行后续业务逻辑。如果此次没有拿到锁,丢弃当前请求直接返回。
  • token令牌
    每次请求操作都会去生成一个唯一的token令牌,服务器端根据该令牌确保相同操作不会被执行多次,具体表现为: 1)每次请求先拿到一个token令牌,下次请求时在请求头带上这个令牌,服务端根据令牌验证判断是否能进行业务操作; 2)验证通过后,删除该令牌,下次请求再次判断token令牌。

要想解决幂等性问题,先要考虑加锁,通过对幂等号(业务唯一ID)进行加锁,之后进行幂等判断,符合条件后再执行业务更新操作。加锁的选择的话,建议选择Redis实现的分布式锁,非阻塞且高效的互斥锁,非常适合幂等控制场景。然后根据操作流水、Token令牌或者状态机判断是否符合幂等条件,最好有操作流水并且能够将幂等号作为唯一性约束。简单代码实例如下:

ini 复制代码
# 加锁
@RequestLock(keyExpression = "#request.traceId", expire = 2000)
public AjaxResult submit(RequestParams request) {
  ObjectResult objectResult = requestService.queryByParams(request);
  # 幂等判断
  if (objectResult == null) {
     # 更新
     return requestService.submit(request);
  }
  return AjaxResult.failure(0, "重复请求");
}

以上即是解决目前在日常开发过程中解决幂等问题的一种方案,大家在使用的过程中根据实际的业务需求,有问题及时发现并解决,选择合适的幂等判断条件进行方案实施,归根结底适合自己的才是最优的。

喜欢本文或者能够给你带来帮助的朋友可以一键三连,谢谢!下篇文章再会~

相关推荐
2401_8827275731 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
心仪悦悦1 小时前
Scala中的集合复习(1)
开发语言·后端·scala
代码小鑫1 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖1 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶2 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
uzong3 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端
飞升不如收破烂~3 小时前
Spring boot常用注解和作用
java·spring boot·后端
何遇mirror3 小时前
云原生基础-云计算概览
后端·云原生·云计算
岁岁岁平安3 小时前
springboot实战(15)(注解@JsonFormat(pattern=“?“)、@JsonIgnore)
java·spring boot·后端·idea
颜淡慕潇4 小时前
【K8S系列】kubectl describe pod显示ImagePullBackOff,如何进一步排查?
后端·云原生·容器·kubernetes