结合案例、列举场景的接口幂等性设计方案。
方案 1. 状态机
业务场景,数据审核成功后进行短信通知,或者是订单状态变成已支付后,短信通知用户订单生成的详细信息,等等和状态有关的操作。
假设 status:0(待审核)、1(已审核)、2(已退回)。
批量审核场景幂等性代码设计
shtz 方法 中的 sql 语句如下:update tab_dzzzhz set status = 1 where (status = 0 or status = 2) and id = #{id}
生效原理:用的是Mysql,Mysql 中的 update 原理,先是对查询条件中命中的数据进行上锁,然后接着修改这条数据。因此当有多个请求打过来,对指定 id、status 的这条数据的更新时,其实是一个串行化操作。第一个请求更新好数据后,数据的 status 变为了 1,第二个请求过来,发现没有符合条件的数据要更新了,直接 return 0(影响的行数为 0)。此时代码中就可以认为此次更新操作是多余的。就避免了重复短信通知的问题
方案 2. redis 分布式锁
这个没啥好说的,就是利用 lua 脚本,保证对应 查询、更新操作 的业务逻辑是原子性的。至于 lua 脚本锁的 key 根据各位的业务逻辑来。
分布式会议室(幂等性校验、同一用户只能报名一次)
方案2与 方案 1 的区别在于,其实就是一个利用 mysql 的行锁、表锁机制实现的,一个是利用 redis 的 lua 脚本实现的。
方案 3.加唯一索引
之前有个业务场景就是必须保证生成,同一个证照编号的数据只能存在一条,而这个数据是掉第三方系统生成的,第三方系统可能存在 bug,同时发俩条一样证照编号的数据过来。此时我们的Mysql 表中由于,对证照编号加了唯一索引,因此在入库的时候,就能进行异常检测了。避免出现重复数据的问题。唯一索引可以是一个字段唯一、也可以是多个字段组合唯一
方案 4.利用Mysql 乐观锁、悲观锁
请挪步:一个小小的乐观锁、悲观锁也能扯这么多
方案 5.建立防重表
进行查询、更新操作之前。第一个请求先把数据插入防重表。后面的并发请求过来插入数据到防重表数据报错。捕获异常直接返回成功。
防重表设计:id+唯一索引。唯一索引可以是多个字段比如:name、code等组合起来的唯一标识,例如:zzh_01。而 mysql 中的 insert 操作,会对 (id+唯一索引) 附近的数据加上间隙锁。多个(id+唯一索引) 的数据并发进行入库时,变成了串行化操作。从而起到了防重的作用。