使用RedisAtomicLong和StringRedisTemplate生成递增原子序列号及其保证原子性的原理
1. 参考链接
2. 为什么要使用redis生成原子序列号,而不是用java自带的atomic包一样进行原子性操作
- AtomicLong只能在一个应用中使用;
- RedisAtomicLong可以在所有与Redis有连接的应用中使用;
3. 为什么能够保证原子性
- RedisAtomicLong的getAndIncrement使用的是redis的incrBy命令。
- redis后台服务是串行的单线程执行,不存在并发,即多线程调用Incr/incrby方法,在redis服务器上仍然是串行的单线程执行,不存在并发,所以这俩命令都是原子自增、线程安全的。
- Redis Incrby 命令将 key 中储存的数字加上指定的增量值;Redis Incr 命令将 key 中储存的数字值增1;
4. 实现代码
@Autowired
StringRedisTemplate redis;
String taskNoKey = "20240706";
// 使用redis原子流水号
long num = this.incr(taskNoKey, redis.getConnectionFactory()) + 1;
/**
* redis流水号自增
*
* @param key 自己设置,保存当前自增值
* @param liveTime 在redis中的缓存时间,方法中设置单位(秒/分/天……)
* @return
*/
// 接受两个参数,key(用于在Redis中存储自增值的键)和RedisConnectionFactory(用于创建Redis连接的工厂)。
public static Long incr(String key, RedisConnectionFactory redisConnectionFactory) {
// RedisAtomicLong创建了一个原子长整型变量,其键为key,连接工厂为redisConnectionFactory
RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisConnectionFactory);
// 使用getAndIncrement()方法获取当前自增值,并将其递增。这个方法是原子操作,保证在多线程环境下的安全性。初始值为0;
Long increment = entityIdCounter.getAndIncrement();
// 如果increment为null或者等于0,这意味着Redis中没有存储该key的值,或者值已经被重置。在这种情况下,代码将设置该key的过期时间为1天。
if ((null == increment || increment.longValue() == 0)) {
entityIdCounter.expire(1, TimeUnit.DAYS); // 设置自增值过期时间,liveTime 过期时间;TimeUnit.DAYS
}
if (increment > 9999) {
// 待处理
}
return increment;
}