分布式锁实例教程之防止重复提交

目录
  • 抛出一个问题
  • 正常的代码逻辑
    • 1、注册controller
    • 2、注册service
    • 3、加入分布式锁,问题依旧
  • 解决方法
    • 分布式锁+防重操作
  • 总结

抛出一个问题

需求:现在有一个常见的场景——用户注册,但是如果出现重复提交的情况,则会出现多条注册数据,因此这里如何做好防止重复提交这是我们需要解决的问题。

正常的代码逻辑

1、注册controller

/**
 * 用户注册请求
 * @param userDto
 * @param bindingResult
 * @return
 */
@RequestMapping(value=prefix+"/db/register",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse register(@RequestBody @Validated UserDto userDto, BindingResult bindingResult){
    BaseResponse response=new BaseResponse(StatusCode.Success);
    try {
        log.debug("注册信息: {} ",userDto);
        //注册之前,我们先判断是否已经注册了。(正常逻辑)
        User user=userService.selectByUserName(userDto.getUserName());
        if (user!=null){
            return new BaseResponse(StatusCode.UserNameExist);
        }
        userService.register(userDto);
    }catch (Exception e){
        e.printStackTrace();
        response=new BaseResponse(StatusCode.Fail);
    }
    return response;
}

在controller中判断用户是否已经注册,如果没有注册,则调用注册逻辑。

2、注册service

/**
 * 用户注册——最普通的操作,没有任何加锁,没有任何防止重复提交
 *
 * @param userDto
 * @return
 * @throws Exception
 */
public int register(UserDto userDto) throws Exception {
    int result = 0;
    User user = new User();
    BeanUtils.copyProperties(userDto, user);
    result = userMapper.insertSelective(user);
    return result;
}

简单的增加一个用户信息。

问题也很明显,这样毕竟会出现问题,并发的问题,也会出现重复注册的情况。测试结果也很明显

一堆重复注册的,但是加入分布式锁就好了么?

3、加入分布式锁,问题依旧

分布式锁的实现方式

/**
 * 用户注册,基于redisson的分布式锁
 *
 * @param userDto
 * @return
 */
public int registerLockRedisson(UserDto userDto) {
    int result = 0;
    RLock rLock = redissonLockComponent.acquireLock(userDto.getUserName());
    try {
        if (rLock != null) {
            User user = new User();
            BeanUtils.copyProperties(userDto, user);
            user.setCreateTime(new Date());
            userMapper.insertSelective(user);
        }
    } catch (Exception e) {
        log.error("获取redisson分布式锁异常");
    } finally {
        if (rLock != null) {
            redissonLockComponent.releaseLock(rLock);
        }
    }
    return result;
}

加入分布式锁之后,再进行测试。

不好意思,依旧出现了重复注册的情况。何解?

问题分析,为了遵循单一职责,这里的读取数据(判断是否注册)与写入数据(用户注册)操作是分开的,分布式锁为了进一步细化,只是加在了写入数据阶段,并没有加在整个业务阶段,因此会出现数据重复提交的问题,解决方法有很多,最暴力的方法无非就是给数据库user表中的用户名字段加入唯一约束。但是这样随着业务规模扩大,数据库压力会越来越大。

解决方法

解决方法有几种,前面提到的给数据库增加唯一索引也是一种方法。但是为了减轻数据库的压力,这种操作可以直接在应用层处理。

分布式锁+防重操作

在分布式锁的基础上,加入redis存储key值,作为防重提交的判断。不想过多解释了,直接上代码吧。

/**
 * 用户注册,redisson分布式锁,redis防止重复提交
 *
 * @param userDto
 * @return
 */
public int registerLockAvoidDupPost(UserDto userDto) {
    int result = 0;
    RLock rLock = redissonLockComponent.acquireLock(userDto.getUserName());
    try {
        //redis中根据用户名存储作为key值
        String key = lockKeyPrefix+userDto.getUserName();
        if (!stringRedisTemplate.hasKey(key)) {//如果不存在key则进入注册阶段
            stringRedisTemplate.opsForValue().set(key,UUID.randomUUID().toString(),10L,TimeUnit.SECONDS);
            User user = new User();
            BeanUtils.copyProperties(userDto, user);
            user.setCreateTime(new Date());
            userMapper.insertSelective(user);
            log.info("{},注册成功",userDto.getUserName());
        }else{//如果存在,则提示不可重复提交
            log.error("10秒内,请勿重复提交注册信息");
        }
    } catch (Exception e) {
        log.error("获取redisson分布式锁异常");
    } finally {
        if (rLock != null) {
            redissonLockComponent.releaseLock(rLock);
        }
    }
    return result;
}

分布式锁的实现方式有多重,redis/redisson/zookeeper等,只需要在已经实现分布式锁的基础上引入防重提交的机制即可。

因此还有其他方式的实现,如下所示为zookeeper分布式锁+redis防重的方式

/**
 * 用户注册,redisson分布式锁,redis防止重复提交
 *
 * @param userDto
 * @return
 */
public int registerLockAvoidDupPost(UserDto userDto) {
    int result = 0;

    InterProcessMutex mutex=new InterProcessMutex(client,zkPrefix+userDto.getUserName()+"-lock");
    try {
        if (mutex.acquire(10L, TimeUnit.SECONDS)){

            final String realKey=zkRedisKeyPrefix+userDto.getUserName();
            if (!stringRedisTemplate.hasKey(realKey)){
                stringRedisTemplate.opsForValue().set(realKey, UUID.randomUUID().toString());

                User user=new User();
                BeanUtils.copyProperties(userDto,user);
                user.setCreateTime(new Date());
                userMapper.insertSelective(user);
				log.info("{},注册成功",userDto.getUserName());
            }else{
                log.error("10秒内,请勿重复提交注册信息");
            }

        }else{
            throw new RuntimeException("获取zk分布式锁失败!");
        }
    }catch (Exception e){
        e.printStackTrace();
        throw e;
    }finally {
        mutex.release();
    }
    return result;
}

测试结果:

并不会出现重复注册情况了。

总结

防重提交不能全部交给数据库

到此这篇关于分布式锁实例教程之防止重复提交的文章就介绍到这了,更多相关分布式锁防止重复提交内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • redis分布式锁解决表单重复提交的问题

    假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到新的页面,这时的用户会再次点击提交按钮,如果没有经过处理的话,这时用户就会生成两份订单,类似于这种场景都叫重复提交. 使用redis的setnx和getset命令解决表单重复提交的问题. 1.引入redis依赖和aop依赖 <dependency> <groupId>org.springframewor

  • 分布式锁实例教程之防止重复提交

    目录 抛出一个问题 正常的代码逻辑 1.注册controller 2.注册service 3.加入分布式锁,问题依旧 解决方法 分布式锁+防重操作 总结 抛出一个问题 需求:现在有一个常见的场景--用户注册,但是如果出现重复提交的情况,则会出现多条注册数据,因此这里如何做好防止重复提交这是我们需要解决的问题. 正常的代码逻辑 1.注册controller /** * 用户注册请求 * @param userDto * @param bindingResult * @return */ @Requ

  • MySQL事务与锁实例教程详解

    目录 MySQL事务和锁 事务 事务的控制语句 事务隔离级别设置 脏读 不可重复读 幻读 锁机制 InnoDB的行级锁 锁实战 死锁 总结 MySQL事务和锁 事务 说到关系型的数据库的事务,相信大家对四大特性都不陌生,分别是原子性.一致性.隔离性.持久性,简称为ACID特性. MySQL中支持3种不同的存储引擎: MyISAM存储引擎.Memory存储引擎.和InnoDB存储引擎 注:只有InnoDB才支持事务. 事务的控制语句 控制语句 作用 begin或者start transaction

  • php基于redis的分布式锁实例详解

    在使用分布式锁进行互斥资源访问时候,我们很多方案是采用redis的实现. 固然,redis的单节点锁在极端情况也是有问题的,假设你的业务允许偶尔的失效,使用单节点的redis锁方案就足够了,简单而且效率高. redis锁失效的情况: 客户端1从master节点获取了锁 master宕机了,存储锁的key还没来得及同步到slave节点上 slave升级为master 客户端2从新的master上获取到同一个资源的锁 于是,客户端1和客户端2同事持有了同一个资源的锁,锁的安全性被打破. 如果我们不考

  • Redis分布式锁实例分析讲解

    目录 1 一人一单并发安全问题 2 分布式锁的原理和实现 2.1 什么是分布式锁 2.2 分布式锁的实现 1 一人一单并发安全问题 之前一人一单的业务使用的悲观锁,在分布式系统下,是无法生效的. 理想的情况下是这样的:一个线程成功获取互斥锁,并对查询订单并创建订单,其他线程无法干预.它的原理是会有一个锁监视器,来监听是谁获得了锁. 但是问题就出现在: 分布式系统下,有多个不同的JVM,不同的JVM的环境下,锁监听器是有多个的,就会出现有的线程在别的线程已经拿到锁的情况下,仍然可以获取的到锁. 这

  • redis实现分布式锁实例详解

    目录 1.业务场景引入 2.基础环境准备 2.1.准备库存数据库 2.2.创建SpringBoot工程,pom.xml中导入依赖,请注意版本. 2.3.application.properties配置文件 2.4.SpringBoot启动类 2.5.添加Redis的配置类 2.6.pojo层 2.7.mapper层 2.8.SpringBoot监听Web启动事件,加载商品数据到Redis中 3.Redis实现分布式锁 3.1分布式锁的实现类 3.2分布式锁的业务代码 4.分布式锁测试 总结 1.

  • Spring Boot使用AOP防止重复提交的方法示例

    在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端.页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性. 上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理. 思路 自定义注解 @NoRepeatSubmit 标记所有Controller中的提交请求 通过AOP 对所有标记了 @NoRepeatSubmit 的方法拦截

  • Java-Redis-Redisson分布式锁的功能使用及实现

    目录 前置 基础设施 功能使用和介绍 其他悲观锁的实现方式 前置 Java-Redis-Redisson配置基础上我们进行了改造,让锁的使用更加方便 基础设施 RedissonLock import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Targ

  • Go 语言下基于Redis分布式锁的实现方式

    分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 项目地址: https://github.com/Spongecaptain/redisLock 1. Go 原生的互斥锁 Go 原生的互斥锁即 sync 包下的 M

  • 深入浅出探索Java分布式锁原理

    目录 什么是分布式锁?它能干什么? 分布式锁实现方案 基于数据库的分布式锁实现方案 实现原理 方案分析 基于Redis的分布式锁实现方案 基于sentnx命令的实现原理 方案分析 基于Redisson实现 RedLock 方案分析 基于Zookeeper的分布式锁实现方案 实现原理 方案分析 分布式锁方案到底选哪个? 总结 什么是分布式锁?它能干什么? 相信大家对于Java提供的synchronized关键字以及Lock锁都不陌生,在实际的项目中大家都使用过.如下图所示,在同一个JVM进程中,T

随机推荐