一.什么是幂等性?
接口幂等性是指在进行多次相同的请求操作时,能够保证系统的状态和结果与执行一次请求操作时的状态和结果相同。换句话说,无论对一个接口进行多少次重复请求,结果都应该是一致的。
接口幂等性对于设计和实现网络服务和API非常重要,特别是在分布式系统中。它能够确保即使在网络通信中出现故障、重试、超时或其他问题时,系统也能保持一致性。
例如在一个网上商城的后台系统中,当用户点击"提交订单"按钮时,可能会发送一个创建订单的请求。如果这个请求是幂等的,那么无论用户点击按钮一次还是多次,系统都只会创建一个订单,并返回相同的结果。这样可以避免因为网络延迟、用户重复点击等原因导致重复创建订单的问题。
二.幂等性方案分析
1.insert前先select
在保存数据的接口中,在insert
前,先进行数据查询。如果该数据已存在,则直接返回,如果不存在,才执行insert操作
2.加唯一索引
加唯一索引是个非常简单但很有效的办法,如果重复插入数据的话,就会抛出异常,为了保证幂等性,一般需要捕获这个异常
3.加悲观锁
更新逻辑,比如更新用户账户余额,可以加悲观锁,把对应用户的哪一行数据锁住。同一时刻只允许一个请求获得锁,其他请求则等待。
select * from user id = 123 for update
这种方式可以保证数据的一致性,缺点是对性能影响较大,其他请求需要等待事务提交,无法马上返回结果
4.加乐观锁
更新逻辑,也可以用乐观锁,性能更好。可以在表中增加一个timestamp
或者version
字段,例如version
,在更新前,先查询一下数据,将version也作为更新的条件,同时也更新version
update user set amount = amount + 100, version = version + 1 where id = 123 and version = 1
前一个请求更新数据时将version值+1,后续请求拿到更新前的version值进行更新,会因version值改变而更新失败
5.建防重表
有时候表中并非所有的场景都不允许产生重复的数据,只有某些特定场景才不允许。这时候,就可以使用防重表的方式。
例如消息消费中,创建防重表,存储消息的唯一ID,消费时先去查询是否已经消费,已经消费直接返回成功
6.状态机
有些业务表是有状态的,比如订单表中有:1-下单、2-已支付、3-完成、4-撤销等状态,可以通过限制状态的流动来完成幂等
例如更新订单完成状态
update order set status = 3 where order_id = 123 and status = 2
7.分布式锁
直接在数据库上加锁的做法性能不够友好,可以使用分布式锁的方式,目前最流行的分布式锁实现是通过Redis,具体实现一般都是使用Redission框架
需考虑因各种原因宕机导致请求中断而造成的分布式锁占用,所以需要对分布式锁的失效时间进行设计
8.token机制
请求接口之前,需要先获取一个唯一的token,再带着这个token去完成业务操作,服务端第二次接收到相同参数的请求来获取token时,可判断token是否存在,从而判断是否是重复的请求