1.什么是接口幂等
接口幂等性就是用户对同一操作发起了一次或多次请求的对数据的影响是一致不变的,不会因为多次的请求而产生副作用。
副作用:可以认为多次请求操作,每一次对数据状态都会产生影响 。
注意这里并没有要求接口返回结果是一致的。
例如:update order set moeny = 100 where orderId = 2029282312
该操作无论执行多少次,对数据的影响都是一致的,不变的。
接口不做幂等处理会怎样?
支付场景:用户购买商品后,发起支付操作,支付系统处理支付成功后,由于网络原因没有及时返回给用户结果,其实这个时候订单已经扣过款,相应的支付流水也都已经生成,这个时候用户又点击支付操作,此时会进行第二次扣款,扣款成功后返回给用户。用户去查看支付订单和流水会发现自己支付两次,完蛋了要用户被投诉了,这就是接口没有处理幂等造成的。
2.什么情况需要处理接口幂等性问题?
2.1 select 天然自带幂等性。
每次查询对数据都不会产生副作用。
2.2 insert 当我们重复插入数据的时候,会出现什么情况 ?
第一种情况:自增主键,没有幂等性。
eg:insert into product_info (id,name,type,price,tm)
执行多次,会新增多条记录。对结果集产生了副作用。
第二种情况:业务主键,具有幂等。
eg:insert into product_info (orderId,name,type,price,tm) orderId 为主键唯一
无论该sql执行多少次,对结果集产生的效果都是一样只增加了一条数据。
2.3 delete 是否具有幂等性?
第一种情况:绝对删除,具有幂等性。
eg;delete from order where id = 3 。
无论该sql执行多少次,对结果集产生的效果都是一样只删除了一条数据。
第二种情况: 相对删除,不具有幂等性。
eg:delete from order where id > 23 .
该操作每执行一次,对结果集产生的结果,可能都不一样,同一操作多次执行对数据产生了副作用。
2.4 update 猜一猜是否具有天热幂等性?
第一种情况:绝对更新,具有幂等性。
eg:update good set stock= 586 where goodId = 10;
该操作无论执行多少次操作对结果的影响都是一样。
第二种情况:相对更新,不具有幂等性。
eg:update good set stock = stock+10 where goodid= 10 ;
每次执行该操作库存数量都会加10,所以不具备幂等操作。
总结:以上都是基于单库,单表的操作幂等性的分析,其实在具体业务当中,可能要设计多个表,多个库,甚至跨服务操作。比如分布式系统中,我们一个接口,可能需要调用多个服务来完成任务。那么这种情况,如何保证接口的幂等性呢?
接口幂等性解决方案
前言:接口幂等处理要根据具体业务来判断怎么处理,以下会举例来阐述接口幂等处理解决方案。
1.token+redis 机制
比如订单支付场景:
该支付分为两个步骤:
1.1 获取全局唯一token
接口处理生成唯一标识(token) 存储到redis中,并返回给调用客户端。
1.2 发起支付操作并附带token
接口处理:
1.2.1 获得分布式锁(处理并发情况)
1.2.2 判断redis中是否存在token
1.2.3 存在 执行支付业务逻辑,否则返回该订单已经支付
1.2.4 释放分布式锁
思考:为什么要加分布式锁?
原因1:在高并发请求中 ,token判断是否存在是非线程安全的,所以要加分布式锁来保证 该条件的判断为线程安全
注释:也可redis用删除操作来判断token,删除成功代表token校验通过 这个删除是原子操作的
原因2:在支付业务中,判断支付订单是否已经存在,存在说明该订单已经支付过了,不存在就执行扣款操作,如果相同操作并发两个请求来到判断条件可能两个请求都能判断支付订单不存在,造成重复扣款。 所以也要加分布式锁保证线程的安全。
2.CAS 保证接口幂等性
2.1 状态机制幂等(状态不可逆)
针对更新操作:
例如 电商订单,订单支付状态 0 待支付 , 1 支付中 , 3 支付成功 4 支付失败。
update order set status = 1 where status =0 and orderId = “201251487987”
该sql语句利用状态CAS 保证该操作的幂等。
eg:比如要进行订单支付,上来先用CAS更新订单状态,
返回影响说为1 代表修改成功,可以支付,继续执行支付业务代码
返回影响数 0 代表修改失败,该订单已经不是待支付订单了。
注释:实际这里是利用CAS原理
3 乐观锁实现幂等
背景由来:
为什么要有幂等这种场景?因为在大的系统中,都是分布式部署,如:订单业务 和 库存业务有可能都是独立部署的,都是单独的服务。用户下订单,会调用到订单服务和库存服务。
比如:订单系统:
订单服务 —> 库存服务 (PRC远程调用(服务接口))
因为分布式部署,很有可能在调用库存服务时,因为网络等原因,订单服务调用失败,但其实库存服务已经处理完成,只是返回给订单服务处理结果时出现了异常。这个时候一般系统会作补偿方案,也就是订单服务再此放起库存服务的调用,库存减1
update t_goods set count = count -1 where good_id=2
这样就出现了问题,其实上一次调用已经减了1,只是订单服务没有收到处理结果。现在又调用一次,又要减1,这样就不符合业务了,多扣了。
幂等这个概念就是,不管库存服务在相同条件下调用几次,处理结果都一样。这样才能保证补偿方案的可行性。
乐观锁方案
借鉴数据库的乐观锁机制,如:
update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
4 防重表
1.利用数据库建一张防重表(加唯一索引)
比如订单支付,
反正重复支付:订单号插入防重表 成功 执行支付业务逻辑,失败说明已经支付过。
防重表支付成功是否要删除:
1.可定期清除数据
2.也可结合 订单状态 ,在支付前查询订单状态为待支付 执行支付操作 ,操作后删除订单号 若 第二个请求插入防重表成功,但是这个时候查询订单状态失败。
(实际这个防重表就是实现了分布式锁)
5. 缓存队列
将请求放入队列,后续使用异步任务处理队列中的数据,过滤掉重复的消息。 和防止重复消费道理是一样。
————————————————
版权声明:本文为CSDN博主「完美天空」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_29978863/article/details/107739744