防重设计

目录

使用场景

如何防重

统一防重(redis;签名;mysql防重表)

插入防重(select+insert;唯一索引;事务回滚)

更新防重(乐观锁;状态机乐观锁)


使用场景

防止接口被多次调用而产生脏数据,比如支付订单出现重复支付

多次调用接口的出现有主观原因比如:人为的重复请求攻击,用户的误操作等;也有客观原因比如:为了健壮性进行超时重试;

  • 人为的重复请求攻击

黑名单+限流;

  • 用户的误操作

提交完按钮置灰;

  • 为了健壮性进行超时重试

保证接口幂等性;

如何防重

统一防重(redis;签名;mysql防重表)

  • redis机制

服务器需要提供获取token机制的服务,这样每次客户端请求的时候先获取token,服务器端会将token保存在redis中;客户端发送请求的时候会带上token,这样服务器端可以拿到token直接去redis中做删除处理,根据返回值判断是否成功:

localhost:0>get token
"111"
localhost:0>del token
"1"
localhost:0>del token
"0"
复制代码

通过redis存放token,这样在分布式环境下也能很好的工作;

  • 签名

根据请求参数生成md5密文,然后用此密文作为key存入redis中,可以通过使用setnx命令来保证只有一个能保存成功;

key = MD5.md5("param1="+param1+"&param2="+param2...)

localhost:0>setnx key 1
"1"
localhost:0>setnx key 1
"0"
复制代码
  • mysql防重表

可以利用表的唯一索引约束,可以使用类似防重key作为唯一索引字段,多次请求过来只会有一个插入成功,为了防止防重表数据过多,可以启动一个定时器定时清理;

以上几种方式其实和具体的业务关系不大,可以适用大部分场景;而且通过redis或者数据库,以及原子性操作来保证在分布式环境下也可以很好的运行;

插入防重(select+insert;唯一索引;事务回滚)

  • select+insert

插入数据最先想到就是先检查有没有,然后在插入,但是这样明显存在两个操作,不是原子性操作,单节点下还能通过锁来解决,分布式环境下就需要用到分布式锁来保证原子性了;当然也可以结合其他方式一起使用,比如下面的唯一主键机制;

  • 唯一主键

这种方式其实就是不直接使用数据库的自增主键了,使用分布式id算法生成,这样在插入数据的时候就可以通过唯一主键来进行约束,保证只会有一条成功;

  • 回滚机制

有些业务其实是有正向流程和逆向流程,比如支付订单,在接收到银行返回的支付成功通知时判断订单的状态,如果已经支付成功,可以直接走退款流程;

更新防重(乐观锁;状态机乐观锁)

  • 乐观锁方式

乐观锁常被用在更新场景中,如下面通过版本号的方式来实现:

update table_name
    sale = sale + 1,
    version = version + 1,
WHERE id = #{id}
AND version = #{version}
复制代码
  • 状态机乐观锁

在相关订单的业务中,很多都会涉及到状态机,状态是流转的,有了上一个状态才会有下一个状态,比如常见的购物订单包括:提交未支付,支付成功,待发货,发货中,已签收等,每个状态都需要前置状态;

update table_name
   set  status = 下一个状态
   where  id = #{id} 
   and status = #{status} 
复制代码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值