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

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

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

什么是幂等?

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

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

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

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

如何保证幂等?

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

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

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

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

相关推荐
zjjsctcdl12 分钟前
springBoot发布https服务及调用
spring boot·后端·https
zdl6861 小时前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情1 小时前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player1 小时前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书
重庆小透明1 小时前
【搞定面试之mysql】第三篇 mysql的锁
java·后端·mysql·面试·职场和发展
前端小张同学1 小时前
有了AI大家的日常是轻松了还是更焦虑了呢?
人工智能·程序员·ai编程
武超杰2 小时前
Spring Boot入门教程
java·spring boot·后端
IT 行者2 小时前
Spring Boot 集成 JavaMail 163邮箱配置详解
java·spring boot·后端
gelald2 小时前
JVM - 运行时内存模型
java·jvm·后端
陈酒尽余欢2 小时前
告别 Vibe Coding:用 SDD 让 AI 编程提效 50%,三工具实战对比
后端·架构