1, 用户多次点击按钮
2, 用户页面回退再次提交
3, 微服务互相调用, 由于网路问题, 导致请求失败, feign触发重试机制
类似于验证码一样, 只有接收的验证码, 和系统生成的验证码一致, 才进行操作
注:
需要使用脚本, 保证以下三个点的原子性
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end
select * from xx where id = 1 for update;
注:
1, 悲观锁, 一般伴随事务一起使用, 数据锁定时间可能会很长, 需要根据实际情况使用;
2, id字段一定是主键或者唯一索引, 不然可能会造成锁表;
update biao set xx = 2 , version = version+1 where xx = 1 and version = 1
注:
1, 使用版本号进行操作
调用接口时, 生成一个唯一id, 比如重试的时候, 使用老请求的原来数据
1, 服务器颁发令牌
String token = UUID.randomUUID().toString().replace("-", "");
//服务器自己保存
redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
//返回给前端
confirmVo.setOrderToken(token);
2, 前端提交请求的时候, 传递给后端防重令牌
3, 后台接收, 进行验证以及业务处理
//1, 验证令牌[核心是: 令牌的对比和删除必须是原子性的]
//因此使用lua脚本, 这个脚本只会返回0(失败) 或者 1(成功)
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
String orderToken = vo.getOrderToken();
//原子验证令牌和删除令牌
Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), orderToken);
if(result == 0){
//令牌验证失败
}else{
//令牌验证通过
}