要在利用中做到幂等,其实并不难,本文尝试做一个系统性的总结,欢送一起探讨。
什么是幂等
某个操作执行一次,跟执行屡次的成果一样。幂等一词来自于数学中的幂等,即 f(f(x)) = f(x)。
须要保障幂等的场景
查问类的读操作,人造是幂等的,屡次调用不会有副作用。需思考以下几种写操作的状况:
- 调用上游写接口
- 写数据库、写 Redis 等
- 音讯订阅和解决
例子:不能给用户反复发放优惠券、现金处分、告诉等,商家更新商品时不能反复减少或缩小库存。
上面别离探讨这几种状况。
1、调用上游写接口
次要依附上游服务保障幂等。
本服务能做的是,在调上游写接口时不做重试,需设置重试次数为 0。
2、本人服务保障
2.1 基于状态的幂等
这种状况比较简单,只有当满足前置条件时才容许操作,否则不容许更新(例如曾经是终态),间接返回。
例子:订单领取胜利后,不容许反复领取。
2.2 基于惟一键的幂等
幂等 key 的选取
与业务强相干,能够是商品 id、订单 id、用户 id,或者日期等,或者是几个业务字段的组合。
几个例子:
- 一个用户每天只能领一张优惠券,通过 用户 id+ 优惠券类型 + 日期字符串 即可惟一标识
- B 端更新库存,商品 id+ 该商品的版本号
- C 端扣库存,订单 id
值得注意的是,须要辨别新增和批改:批改时的幂等 key 往往须要带上版本号 ,能力辨别是否同一次批改,每次批改对应一个惟一的版本号。
实现形式
MySQL 表中为幂等 key 建设惟一索引:强幂等,例如资金、订单,相对不容许反复解决,当插入反复数据时报错。
不举荐用 Redis 实现幂等,一旦 Redis 出问题,比方节点宕机,可能呈现 2 个 client 同时获取到锁的状况。
MySQL 幂等伪代码:
插入重复记录,捕捉异样,提醒幂等拦挡。
try {
// 插入记录
someDao.create(someRecord);
} catch (DataIntegrityViolationException e) {
// 如果是重复记录,返回异样
return failResponse("幂等拦挡");
} catch (Throwable t) {
// 异样解决
return failResponse("其余异样");
}
3、音讯订阅和解决
MQ 通常会保障音讯至多发送一次(可能屡次),并且在机器实例重启或发版时,consumer group 会做 rebalance,进而收到反复的音讯。因而,音讯的幂等解决必不可少。
实现形式:
在解决音讯前加上 Redis 锁:如果上锁胜利,则持续解决,否则稍后重试。
- setnxex,不存在时才设置,时效即为锁的租期,否则疏忽
- 接下来的业务解决,如果是本身逻辑须要强幂等则应用上述数据库幂等形式,如果全副依赖上游则依赖上游实现幂等
Redis 幂等伪代码:
// 生成幂等 key
String redisKey = buildRedisKey();
// 上 Redis 锁,租期为 leaseTime
if (redisLock.tryLock(redisKey, leaseTime)) {// 业务逻辑解决} else {// 稍后重试}