接口如何保证幂等性的解决方案
目录
- 前言
- 接口幂等性概念
- 保证接口幂等性的解决方案
- 查询操作和删除操作
- `insert` 前先 `select`(防重复)
- 使用唯一索引(防重复)
- 使用悲观锁
- 使用乐观锁
- 使用分布式锁
- 使用状态机
- 使用 `Token` 机制
- 总结
前言
在实际项目中有很多操作,不管是做多少次,都应该产生一样的效果或返回一样的结果
- 前端重复提交选中的数据,后台应该只产生对应这个数据的一个反应结果
- 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统
bug
重发,也应该只扣一次钱 - 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的
- 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题等等很多重要的情况都需要幂等的特性来支持
接口幂等性概念
接口幂等性:是指用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用或不同的结果。简言之,幂等性就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的
防重复和幂等,其实是有区别的
- 防重复主要为了避免产生重复数据,对接口返回没有太多要求
- 幂等除了避免产生重复数据之外,还要求每次请求都返回一样的结果
保证接口幂等性的解决方案
查询操作和删除操作
- 对于查询操作:查询一次和查询多次,在数据不变的情况下,查询结果是一样的。
select
是天然的幂等操作 - 对于删除操作:删除操作也是幂等的,删除一次和多次删除都是把数据删除
insert
前先 select
(防重复)
通常情况下,在保存数据的接口中,我们为了防止产生重复数据,一般会在 insert
前,先 select
一下数据。如果该数据已存在,则执行 update
操作,如果不存在,才执行 insert
操作
该方案可能是我们平时在防止产生重复数据时,使用最多的方案。但是该方案不适用于并发场景
使用唯一索引(防重复)
绝大数情况下,为了防止重复数据的产生,我们都会在表中加唯一索引,这是一个非常简单,并且有效的方案
alter table `order` add UNIQUE KEY `un_code` (`code`);
加了唯一索引之后,第一次请求数据可以插入成功。但后面的相同请求,插入数据时会报 Duplicate entry '002' for key 'order.un_code
异常,表示唯一索引有冲突。虽说抛异常对数据来说没有影响,不会造成错误数据。但是为了保证接口幂等性,我们需要对该异常进行捕获,然后返回成功
使用悲观锁
在支付场景中,用户 A
的账号余额有 150
元,想转出 100
元,正常情况下用户 A
的余额只剩 50
元
update user amount = amount-100 where id=123;
如果出现多次相同的请求,可能会导致用户 A
的余额变成负数,这是很严重的系统 bug
为了解决这个问题,可以加悲观锁,将用户 A
的那行数据锁住(行级锁),在同一时刻只允许一个请求获得锁,更新数据,其他的请求则等待
select * from user id=123 for update;
注意:对于 mysql
而言,存储引擎必须用 innodb
,id
字段一定要是主键或者唯一索引,不然就会锁表。行级锁并不是锁行记录,而是锁索引
悲观锁需要在同一个事务操作过程中锁住一行数据,如果事务耗时比较长,会造成大量的请求等待,影响接口性能
使用乐观锁
使用数据版本(version
)记录机制实现,这是乐观锁最常用的一种实现方式。一般是通过为数据库表增加一个数字类型的 version
字段来实现
- 当读取数据时,将
versions
字段的值一同读出,数据每更新一次,对此version
值加1
- 当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的
version
值进行比对,如果数据库表当前版本号与第一次取出来的version
值相等,则予以更新,否则认为是过期数据
update product set productCount = productCount - 1, version = version + 1 where productId = 1 and version = 1
注意:productId
字段一定要是主键或者唯一索引,不然就会锁表
使用分布式锁
如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方组件(redis
或 zookeeper
),在业务系统插入数据或更新数据之前,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路
注意:分布式锁一定要设置一个合理的过期时间,如果设置过短,无法有效的防止重复请求。如果设置过长,可能会浪费 redis
的存储空间,需要根据实际业务情况而定
使用状态机
很多时候业务表是有状态的,比如订单表中有:1-下单、2-已支付、3-完成、4-撤销
等状态。如果这些状态的值是有规律的,按照业务节点正好是从小到大,我们就能通过它来保证接口的幂等性
假如 id=123
的订单状态是已支付,现在要变成完成状态
update `order` set status=3 where id=123 and status=2;
- 第一次请求时,该订单的状态是已支付,值是
2
,所以该update
语句可以正常更新数据,sql
执行结果的影响行数是1
,订单状态变成了 `3 - 后面有相同的请求过来,再执行相同的
sql
时,由于订单状态变成了3
,再用status = 2
作为条件,无法查询出需要更新的数据,所以最终sql
执行结果的影响行数是0
,即不会真正的更新数据
但为了保证接口幂等性,影响行数是 0
时,接口也可以直接返回成功
使用 Token
机制
该方案跟之前的所有方案都有点不一样,需要两次请求才能完成一次业务操作
第一次请求获取
token
第二次请求带着这个
token
,完成业务操作
总结
幂等与你是不是分布式高并发还有 JavaEE
都没有关系。关键是你的操作是不是幂等的。一个幂等的操作典型如:把编号为 5
的记录的 A
字段设置为 0
这种操作不管执行多少次都是幂等的。一个非幂等的操作典型如:把编号为 5
的记录的 A
字段增加 1
这种操作显然就不是幂等的。要做到幂等性,从接口设计上来说不设计任何非幂等的操作即可
接口如何保证幂等性的解决方案相关推荐
- 分布式系统接口如何保证幂等性
分布式系统接口如何保证幂等性 转载请注明出处:https://www.cnblogs.com/jajian/p/10926681.html 业务场景 公司有个借贷的项目,具体业务类似于阿里的蚂蚁借呗, ...
- 点一下按钮调两次接口?浅谈接口设计的幂等性
作者 l 会点代码的大叔(CodeDaShu) 在单体架构时代,就存在着接口幂等性的问题,只不过到了分布式.高并发的场景之后,接口幂等性的问题会更加明显. 01 幂等性的概念 那么什么是幂等性呢? 当 ...
- Java接口幂等性多种解决方案
Java接口幂等性的解决方案: java 语音中,同一个接口相同的参数多次和一次请求产生的效果是一样,这样的过程即被称为满足幂等性 //这中情况无论执行多少次,结果都不受影响,是幂等的. update ...
- 接口设计的幂等性考虑
分布式系统接口幂等性 1.幂等性定义 1.1 数学定义 在数学里,幂等有两种主要的定义:- 在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素.例如,乘法下唯一两个 ...
- 线上故障之-redis锁处理幂等性失效和幂等性问题解决方案
线上故障之-redis锁处理幂等性失效和幂等性问题解决方案 redis锁处理幂等性失效 事务传播bug try bug 幂等性设计方法 1. insert前先select 2. 加悲观锁 3. 加乐观 ...
- window.location.href如何多次请求_RabbitMQ如何保证幂等性?
有个哥们去年参加阿里面试的时候,就被问到了幂等性的问题. 幂等性是分布式系统设计中的一个重要概念,是在做系统或者接口设计时要着重考虑的问题,尤其像支付宝.银行.互联网金融等涉及钱的系统,既要高效,数据 ...
- 【MQ】Kafka如何保证幂等性
文章目录 幂等性要解决的问题? Kafka 是怎么保证幂等性的? 开启幂等性配置 Kafka幂等性的局限性 事务 kafka默认情况下,提供的是至少一次的可靠性保障.即broker保障已提交的消息的发 ...
- 消息队列、RabbitMQ原理、消息队列保证幂等性,消息丢失,消息顺序性,以及处理消息队列消息积压问题
消息队列 消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已 常见的消息队列 RabbitMq ActiveM ...
- kafka怎么保证数据消费一次且仅消费一次?使用消息队列如何保证幂等性?
精确一次处理语义(exactly onece semantic–EOS),Kafka的EOS主要体现在3个方面: 1)幂等producer 保证单个分区的只会发送一次,不会出现重复消息 2)事务(tr ...
- java幂等性的解决方案
java幂等性的解决方案 参考文章: (1)java幂等性的解决方案 (2)https://www.cnblogs.com/xinruyi/p/11441818.html (3)https://www ...
最新文章
- 将普通路由器设置成微信连WiFi路由器的方法
- UA MATH571B 试验设计III 单因素试验设计3
- Apache防DDOS模块mod_evasive的安装配置和使用
- 前端小知识点(1):undefined和null区别
- Django项目部署在Centos7
- 数据结构实验之栈六:下一较大值(二)
- Java编程技巧之样板代码
- mysql联合索引like_MySQL全文索引、联合索引、like查询、json查询速度大比拼
- java地址传递_关于java中是地址传递还是值传递的测试
- 四、CXF WebService中传递复杂类型对象
- c3p0存在严重bug “APPARENT DEADLOCK“的问题
- Android性能优化---布局优化
- Java项目部署到云服务器最简单的方法
- etc 文件夹下放什么内容
- 思科无线POC测试要包含哪些测试项
- 计算机网络中型网吧规划设计,中小型网咖网络规划设计开题报告
- python爬虫能爬取微信密码吗_爬虫如何爬取微信公众号文章
- 蓝桥杯第四届C/C++ B省赛题目及题解
- 22考研全程时间计划安排表!
- 软件测试 | 测试开发 | Git实战(四)| Git分支管理实操,搞定在线合并和本地合并