新增数据并发处理,避免重复数据插入

写在前面

        项目使用SpringBoot的多实例微服务

层级调用Controller -->Service-->Dao或者Mapper(Mybatis)

具体问题和解决方案

        最近在做一个模块的时候,有一个新增接口可能存在重复插入的问题

QA人为模拟两个人同时操作,具体做法是用两个手机登录同一个界面同时操作,这样就造成了重复插入,

在数据库层面,为了避免重复数据,我们可以建立唯一索引,但建立唯一索引后,程序就会抛出唯一性异常,需要捕获处理

因为是多实例(即部署多个节点)的微服务,所以解决重复问题不能用同步锁、局部锁(RetreenLock)的方式来处理,所以就考虑用分布式锁,因为项目中即成了Redis,所以第一个想到的就是用Redis分布式锁来处理,也确实解决了问题。

        那么需要注意的点是:

1、在Service实现类中,不能将查询和新增操作放在同一个方法中,因为同一个方法在同一个事务中,比如下面的模拟代码:

 
  
@Transactional
public List<对象> test(){

        //其他代码省略。。。

        save(新增插入数据库操作)

        return select(查询刚新增的数据)

}

上面这段代码的问题是,新增和查询都在一个方法中,只有方法之行完成后,事务才会真正的提交,那么在方法中查询到的数据很可能就是没提交过数据前的数据,所以查询出来的结果就是不对的

        那么正确的做法就是将save(新增)实现和select(查询实现)放在不同的方法中,在Controller层去分别调用,这样查询数据就能查到最新新增的数据,因为事务已经提交了

Controller层,将新增方法上锁,这里用到的是redis分布式锁,实现起来也非常的简单,但实用:

RLock lock = redissonClient.getLock(key);
try {
    lock.lock();

    可以在这里做个简单的查询

    save(新增实现)

}finally{

        lock.unlock();

}

然后在查询数据就能查询到最新保存的数据了,因为事务已经提交了

这样就搞定了

        当然也可以用其他的分布式锁,比如创建Zookeeper的临时节点来作为锁,

这里不太推荐用数据库锁,除非数据量非常小,用户量也少,操作不频繁。

你可能感兴趣的:(后端,微服务,并发,java,微服务,spring,boot)