使用RedisAtomicLong和StringRedisTemplate生成递增原子序列号及其保证原子性的原理

373 阅读2分钟

使用RedisAtomicLong和StringRedisTemplate生成递增原子序列号

1. 参考链接

2. 为什么要使用redis生成原子序列号,而不是用java自带的atomic包一样进行原子性操作

  1. AtomicLong只能在一个应用中使用;
  2. RedisAtomicLong可以在所有与Redis有连接的应用中使用;

3. 为什么能够保证原子性

  1. RedisAtomicLong的getAndIncrement使用的是redis的incrBy命令。
  2. redis后台服务是串行的单线程执行,不存在并发,即多线程调用Incr/incrby方法,在redis服务器上仍然是串行的单线程执行,不存在并发,所以这俩命令都是原子自增、线程安全的。
  3. 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;
    }