如何保证接口幂等性
1、幂等性是什么?
接口幂等性
是指用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了不同的结果。
2、使用幂等性的场景有哪些?
- 页面点击保存按钮时,不小心快速点了两次,表中竟然产生了两条重复的数据,只是id不一样。
- 我们在项目中为了解决
接口超时
问题,通常会引入了重试机制
。第一次请求接口超时了,请求方没能及时获取返回结果(此时有可能已经成功了),为了避免返回错误的结果(这种情况不可能直接返回失败吧?),于是会对该请求重试几次,这样也会产生重复的数据。 - 消息重复消费,在使用消息中间件来处理消息队列,且手动 ack 确认消息被正常消费时。如果消费者突然断开连接,那么已经执行了一半的消息会重新放回队列。当消息被其他消费者重新消费时,如果没有幂等性,就会导致消息重复消费时结果异常,如数据库重复数据,数据库数据冲突,资源重复等。
3、如何保证接口幂等性?
- 悲观锁
- 乐观锁(版本号)
- 唯一索引
- 分布式锁
- 去重表
- token机制
4、详解具体实现
4.1、insert前先select(无法处理高并发)
通常情况下,在保存数据的接口中,我们为了防止产生重复数据,一般会在insert
前,先根据name
或code
字段select
一下数据。如果该数据已存在,则执行update
操作,如果不存在,才执行 insert
操作。
该方案可能是我们平时在防止产生重复数据时,使用最多的方案。但是该方案不适用于并发场景,在并发场景中,要配合其他方案一起使用,否则同样会产生重复数据。
4.2、insert前先select并配合版本号,支持高并发(乐观锁)
-
先根据id查询用户信息,包含version字段
-
根据id和version字段值作为where条件的参数,更新用户信息,同时version+1
-
判断操作影响行数,如果影响1行,则说明是一次请求,可以做其他数据操作。
-
如果影响0行,说明是重复请求,则直接返回成功。
4.3、token机制
通过token 机制实现接口的幂等性,这是一种比较通用性的实现方法。
示意图如下:
具体流程步骤:
-
客户端会先发送一个请求去获取 token,服务端会生成一个全局唯一的 ID 作为 token 保存在 redis 中,同时把这个 ID 返回给客户端。
-
客户端第二次调用业务请求的时候必须携带这个 token。
-
服务端会校验这个 token,如果校验成功,则执行业务,并删除 redis 中的 token。
-
如果校验失败,说明 redis 中已经没有对应的 token,则表示重复操作,直接返回指定的结果给客户端
4.5、去重表
这种实现方式是利用 mysql 唯一索引的特性。示意图如下:
具体流程步骤:
-
建立一张去重表,其中某个字段需要建立唯一索引
-
客户端去请求服务端,服务端会将这次请求的一些信息插入这张去重表中
-
因为表中某个字段带有唯一索引,如果插入成功,证明表中没有这次请求的信息,则执行后续的业务逻辑
-
如果插入失败,则代表已经执行过当前请求,直接返回。
4.6、分布式锁
这种实现方式是基于 SETNX 命令实现的。
SETNX key value:将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。该命令在设置成功时返回 1,设置失败时返回 0。示意图如下:
具体流程步骤:
-
客户端先请求服务端,会拿到一个能代表这次请求业务的唯一字段
-
将该字段以 SETNX 的方式存入 redis 中,并根据业务设置相应的超时时间
-
如果设置成功,证明这是第一次请求,则执行后续的业务逻辑
-
如果设置失败,则代表已经执行过当前请求,直接返回