接口幂等性
- 如何实现幂等性
- 实现方式一:数据库唯一主键
- 实现方式二:Token机制
- 实现方式三:数据库乐观锁
- 实现方式四、加分布式锁
如何实现幂等性
其实实现幂等性的方案有不少,但是呢,这就得需要你根据不同的业务场景去选择合适的方式了。
实现方式一:数据库唯一主键
加唯一索引可以保证同时有多个请求过来的时候只有一个请求可以执行插入操作,因为当一个请求已经插入了数据之后,其他请求再去插入数据的时候因为有唯一索引所以就会报错。为了保证幂等性,如果一个请求得到的结果是报错的话,我们后端要对这种异常进行处理来保证给前端返回的结果是请求成功,因为要保证多次请求返回的结果是一样的。
但是唯一索引的添加也是一个技术活,如果我们对一个单一的字段设置为唯一索引的话,如果你的这 个业务场景中以进行数据的真正删除的话那么这么设计是没有问题的,但是如果你的业务中要求你的数据不能真正的删除而是需要进行留档,用户的删除只是进行逻辑删除的话,我们设计的单一的字段为唯一索引的话,那么我们插入新数据的时候就会出现唯一索引冲突的问题了。
那么对于这种业务场景我们就要采用符合索引了,也就是将多个字段作为唯一索引,比如我们将 order_id和is_delete字段作为一个唯一索引。但是这种设计也是有问题的,比如如果我们的系统中的逻辑删除字段(is_delete)的值只有0和1,0代表没有删除,1代表删除了,那么这样的话,我们再插入新的数据的时候还是可能会出现唯一索引冲突的。那么解决这个问题的方案是什么呢?那就是我们可以扩大 is_delete字段的值,比如如果这个字段已经删除了,我们将is_delete的值设置为这一行数据的唯一主键id, 如果没有删除的话就是0.那么这样的话就不会出现唯一索引冲突了。
加唯一索引的流程图:
实现方式二:Token机制
Token机制,实际上也可以称为 Token 令牌
- 服务端提供了发送token的接⼝。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在 执⾏业务前,先去获取token,服务器会把token保存到redis中。(微服务肯定是分布式了,如果 单机就适⽤jvm缓存)。
- 然后调⽤业务接⼝请求时,把token携带过去,⼀般放在请求头部。
- 服务器判断token是否存在redis中,存在表示第⼀次请求,这时把redis中的token删除,继续执⾏业务。
- 如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执⾏。
实现方式三:数据库乐观锁
使用乐观锁的话,我们需要在数据库中设计一个用来作为版本号的字段,比如增加一个timeStamp或 者version字段来作为数据库的字段,我们在每次更新操作的时候会判断这个版本号,只有版本号是一致的才可以进行更新,否则是无法进行更新的。
比如下面的sql流程
#1.查询我们要操作的数据,获得当前这行数据的版本号
select id,money,version from user where id=1;
#2.根据第一步获得的版本号来进行更新操作。
update user set money=money-100,version=version+1 where id=1 and version=1;
如果此时返回的结果是1说明操作成功了,如果返回的数据是0的话,说明当前这个请求没有进行处 理。通过这个乐观锁保证了多个请求同时过来的时候只有一个请求可以执行。同时为了保证幂等性,如果返回的结果是0的话,我们也要返回成功的结果,因为幂等性要保证多次请求返回的结果是相同的。
实现方式四、加分布式锁
现在比较常用的分布式锁的框架就是Redis和zookeeper了。下面我来说一下redis中的方法。
- setnx命令
- set命令
- redisson
多个请求过来的时候我们只让第一个请求可以设置key,并且给这个key设置一个过期时间,后面再过来的请求发现已经有这个key了就无法设置key了。所以成功设置了key的请求就是成功执行的那个请求,后面没有成功设置key的请求就是没有成功执行的请求,但是我们还是要给前端返回成功的结果,因为要保证幂等性。
zookeeper的解决方案的话,使用Curator框架就可以了。
分布式锁方案的流程图