赞
踩
项目使用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的临时节点来作为锁,
这里不太推荐用数据库锁,除非数据量非常小,用户量也少,操作不频繁。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。