什么是幂等性?
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在数学中,主要有两个定义
如果在一元运算中,x 为某集合中的任意数,如果满足 f(f(x))=f(x),那么该 f 运算具有幂等性,比如绝对值运算就是幂等性函数。
如果在二元运算中,x 为某集合中的任意数,如果满足 f(x,x)=x,前提是 f 运算的两个参数均为 x,那么我们称 f
运算也有幂等性,比如求大值函数就是幂等性函数。
在编程领域里,幂等操作的特点是
其任意多次执行所产生的影响均与一次执行的影响相同。
业务上理解幂等性不仅仅只是一次或多次操作对资源产生相同影响,还包括第一次操作产生影响后,以后多次操作不会再产生影响。举例:服务端会进行重试等操作或客户端有可能会进行多次点击提交。如果这样请求多次的话,那最终处理的数据结果就一定要保证统一,如支付场景或者下单。此时就需要通过保证业务幂等性。
幂等的维度
- 时域唯一性
定义幂等的有效期。有些业务需要永久性保证幂等,如下单、支付等。而部分业务只要保证一段时间幂等即可。
- 空域唯一性
定义了幂等的范围,如生成订单的话,不允许出现重复下单。 一次操作=服务方法+传入的业务数据
HTTP协议语义幂等性
http1.1文档:https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
在HTTP/1.1中,对幂等性进行了定义。它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果(错误或网络超时问题除外)。也就是其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
从这边可以看出GET、HEAD、PUT、DELETE是幂等的,POST、PATCH是非幂等的。
为什么出现幂等性问题
在接口调用时一般情况下都能正常返回信息不会重复提交,不过在遇见以下情况时可以就会出现问题:
- 前端重复提交表单:用户在点击一些按钮时,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户一直点提交按钮,这时就会发生重复提交表单请求。或者用户进行恶意刷单,导致接口接收到重复提交的请求。
- 接口超时重复提交:很多时候 HTTP客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。
- 消息进行重复消费: 当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。
方案一:数据库主键或唯一索引
数据库主键的实现主要是利用数据库中主键唯一约束的特性,一般来说唯一主键比较适用于“插入”时的幂等性,其能保证一张表中只能存在一条带该唯一主键的记录。
使用数据库唯一主键完成幂等性时需要注意的是,该主键一并不是使用数据库中自增主键,而是使用分布式 ID 充当主键,这样才能能保证在分布式环境下 ID 的全局唯一性。
或者使用唯一索引,避免脏数据,无法插入重复数据,保证数据唯一性。
适用于插入、删除操作
方案二:数据库乐观锁
数据库乐观锁不仅可以用来当分布式锁,也可以用来幂等处理。
用法就是对应的数据表中加入version字段,每次查询时将其查出,每次更新时先判断version是否还是之前查出的值,如果是的话,就进行操作;否则,不进行操作。
适用于更新操作。
方案三:防重 Token 令牌
简单的说就是调用方在调用接口的时候先向后端请求一个全局 ID(Token),请求的时候携带这个全局 ID 一起请求(Token 最好将其放到 Headers 中),后端需要将这个 Token 作为 Key,用户信息作为 Value 到 Redis 中进行键值内容校验,如果 Key 存在且 Value 匹配就执行删除命令,然后正常执行后面的业务逻辑。如果不存在对应的 Key 或 Value 不匹配就返回重复执行的错误信息,这样来保证幂等操作。
适用于插入、更新、删除操作
方案四:上游服务传唯一的分布式ID
上游服务传一个分布式唯一ID,在调用下游服务接口时,带上这个唯一ID。
下游服务接收到ID后,使用redis的setnx指令,设置对应的超时时间,将ID作为key传入,:
如果执行成功,说明不是重复请求,可以进行执行;
如果执行失败,说明是重复请求,直接返回。