在业务开发中遇到一个场景,测试人员和我同时点新增某记录时,库里出现了一模一样的数据,实际上,应该按照需求来说,该记录的中文名称应该要不一样。(该记录必须要有中文名称,并且要按照一定规则进行拼接,所以同一页面被多个人打开时,同时操作,会出现拼接的中文名不唯一)
需求:点击新增时,自动为该记录按照规则命名中文名称。
实现:查询上一条该记录的同类,获取同类名称,然后按照规则,命名新增的记录,最后插入记录。
异常:该页面两人及以上同时操作时,点击新增记录,导致出现同名的记录。
解决:加redis分布式锁。
redis分布式锁实现工具类
/**
* 分布式锁实现
*
* @param lockName 锁名称
* @param businessId 业务ID
* @param handle 业务处理
*/
@Transactional(rollbackFor = Exception.class)
public void tryLockException(String lockName, Object businessId, VoidHandle handle) {
RLock rLock = getLock(lockName, businessId);
if (!rLock.tryLock()) {
log.error("Acquire a distributed lock, lockName: [{}], businessId: [{}] fail, will throws runtime exception", lockName, businessId);
throw new RuntimeException("Acquire a distributed lock");
}
try {
log.debug("Acquire a distributed lock, lockName: [{}], businessId: [{}] success", lockName, businessId);
handle.execute();
} finally {
rLock.unlock();
}
}
/**
* 获取锁
*
* @param lockName
* @param businessId
* @return
*/
private RLock getLock(String lockName, Object businessId) {
if (StrUtil.isBlank(lockName)) {
throw new RuntimeException("distributed lock key is blank");
}
if (Objects.isNull(businessId)) {
throw new RuntimeException("business ID is null");
}
String lockKey = lockName + businessId.toString();
return redissonClient.getLock(lockKey);
}
业务实现
service实现层
@Override
@Transactional(rollbackFor = {Exception.class, RuntimeException.class})
public Boolean add(InsertVO insertVo) {
//bean转换
Test test= OrikaBeanUtil.map(insertVo, Test.class);
//分布式实现 用部门id做businessId
redissonLock.tryLockException("insert:", insertVo.getDepartmentId(), () -> {
infoManager.insert(test);
});
return true;
}
manager实现层(DAO层)
@Override
public Long insert(Test test) {
//获取上一次相关的数据
Test testInfo = getLastInfo(test);
//设置名称
test.setName(Objects.isNull(testInfo ) || StrUtil.isBlank(testInfo .getName()) ? NameUtil.INIT_NAME : NameUtil.getNextName(testInfo .getName()));
return mapper.insert(test);
}
总结:加上该锁后,多人操作时,会去判断是否有相同记录是否正在插入中(根据部门id或其他作为判断),保证数据不会重复。
所以有遇到相同情况:数据入库时,需要查询以前数据作为依据再入库,但多人操作时发生数据重复入库问题,可以考虑使用锁去解决,我这里是使用redis锁进行解决的。