幂等相关(二)
上文参考了网上的一些资料,本文将针对一些场景说说自己的理解。
一般请求或者MQ读取消息时,可以对业务数据的类型,状态等进行有效性校验,更新的时候利用状态机更新。
也可以利用利用唯一流水号和防重表【直接insert + 主键/唯一索引冲突】的方法,直接防重。当然为了防止防重表过大,可以定时的删除一定时间前的数据。(MQ的时候,唯一流水号可以在生产端生成,随着业务数据一起推送)
对于注册这种业务,可以明确确定唯一索引字段,比如手机号或者手机号+租户号,可以直接做到插入的数据防重。
对于下单这种业务,不能确定 同一个用户+相同的下单商品 就是唯一数据,这种情况防重的话,除了前端做二重提交的处理外,后端接口我能想到的有一下几种思路:
前端在订单提交页生成唯一流水号,和业务数据一起作为参数传给生成订单接口,接口利用防重表,插入成功的情况下,继续处理。因唯一索引插入失败的情况下,则视为重复提交数据,直接返回,不做后续处理。
和1一样,在订单提交页生成唯一流水号作为参数传给生成订单接口,接口利用唯一流水号作为分布式锁对象,能够获取到锁,继续处理,获取不到锁,则视为重复提交数据,直接返回,不做后续处理。当然这种方法只适合高并发的场景,若同一个流水号的数据再第一次执行完成锁释放后,再收到第二次请求,则同样能够再执行一次。
1的基础上,将防重表换成redis缓存。收到请求后,判断缓存是否存在唯一流水号,存在则视为重复提交数据,直接返回,不做后续处理。不存在的情况下,将唯一流水号作为key存入redis,进行后续处理。当然,判断redis中存在和存入redis两个操作合起来必须是原子的。存的时候可以设置个有效期,比如一天。
业务上牺牲下用户的下单间隔,如5s内一个用户不能下两次单。接口则可以对用户id为单位进行5s加锁,这样一定时间内就不会生成两个订单。但这种方式只是从高并发上限制重复请求,弊端很大(本来不想写的,就是为了列下弊端),一个就是业务上也不会允许牺牲下单时间间隔,还有对用户id单位的加锁力度比较大,再者也是比较致命的,比如两个重复请求就是再间隔时间后发的,还是会造成重复数据。
