用分布式锁和redis实现原子性递增,解决编号重复问题
原子性递增
·
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.boot.starter.lock.client.RedissonLockClient;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.system.counter.entity.Counter;
import org.jeecg.modules.system.counter.mapper.CounterMapper;
import org.jeecg.modules.system.counter.service.ICounterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import javax.transaction.Transactional;
import java.util.Objects;
/**
* @Description: 计数器
* @Author: jiangfeng
* @Date: 2022-07-03
* @Version: V1.0
*/
@Service
@Slf4j
public class CounterServiceImpl extends ServiceImpl<CounterMapper, Counter> implements ICounterService {
@Autowired
private ICounterService counterService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedisUtil redisUtil;
@Autowired
RedissonLockClient redissonLock;
@Override
public String tableCodeCounter(String key, int codeLength, String prefix) throws Exception{
String finalCode;
// 从 redis 中获取一个自增的计数器
long incrementCounter = RedisAutoIncr(key);
// 格式化编码长度,不包括前缀
String formatCounter = String.format("%0" + codeLength + "d", incrementCounter);
// 生成后的计数器
if (StringUtils.isNotEmpty(prefix)) {
finalCode = prefix + formatCounter;
} else {
finalCode = String.valueOf(incrementCounter);
}
// 保存至数据库
boolean isSubmit = saveToTable(key, codeLength, prefix, incrementCounter);
if (!isSubmit) {
throw new Exception("数据库保存失败");
}
return finalCode;
}
@Transactional
public boolean saveToTable(String key, int codeLength, String prefix, long counter){
// 兼容数据库此处使用查询来判断数据库是否存在数据
boolean submit;
QueryWrapper<Counter> queryWrapper= new QueryWrapper<>();
queryWrapper.eq("field_name", key);
Counter countValue = counterService.getOne(queryWrapper);
Counter entity = new Counter();
if (countValue == null) {
//不存在,新增
entity.setFieldName(key);
entity.setCodeLength(codeLength);
entity.setPrefix(prefix);
entity.setCounter(counter);
submit = counterService.save(entity);
} else {
//存在,修改
entity.setId(countValue.getId());
entity.setCounter(counter);
submit = counterService.updateById(entity);
}
return submit;
}
/**
* @description 生成分布式唯一自增Id
* @param key
*/
public Long RedisAutoIncr(String key) { // 初始值是从1 开始 还是从0开始?
// 加Redis分布式锁 双重保险,防止初始化的时候并发【不存在会有两种情况,一种是没有这个key的自增,一种是数据库中有,redis过期了或重启了】
RedisAtomicLong redisAtomicLong;
if (redisUtil.get(key) == null) {
// 仅在key为空的时候加锁,不影响执行效率
try {
redissonLock.tryLock(key+ "_redisLock", -1, 3000);
//查询是否在数据库中存在
Counter counter = counterService.getOne(new QueryWrapper<Counter>().eq("field_name", key));
if (counter!=null) {
// 从数据库赋值后,redis初始化不自增
redisUtil.set(key, counter.getCounter()+1);
}
redisAtomicLong = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
// redisAtomicLong.persist();
// -1 为不过期
log.warn(String.valueOf(redisAtomicLong.getExpire()));
// 解锁
redissonLock.unlock(key+ "_redisLock");
} catch (Exception e){
// 异常了,解锁
redissonLock.unlock(key+ "_redisLock");
throw e;
}
} else {
// 如果存在key ,increment 是原子性的,不会重复计数
redisAtomicLong = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()) );
}
long Incr = redisAtomicLong.getAndIncrement();
log.info("Incr------:" + Incr);
return Incr;
}
}
更多推荐
已为社区贡献2条内容
所有评论(0)