Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis分布式唯一编号生成工具类

基于Redis实现的分布式唯一编号生成工具类

作者:她说..

这篇文章主要介绍了基于Redis实现的分布式唯一编号生成工具类,核心功能是生成格式为 业务编码+日期+3位自增序号(如 JJ20250826001)的全局唯一编号,适用于分布式系统中需要有序、不重复编号的场景(如订单号、单据号等),以下是详细解析,需要的朋友可以参考下

首先,直接上代码:

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import com.baomidou.mybatisplus.core.toolkit.DateUtils;
import com.xxxxx.blade.redis.BladeRedis; // 根据实际包路径调整
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import com.xxxxx.common.exception.ServiceException; // 根据实际包路径调整

/**
 * 通用编号生成工具类
 * 功能:生成格式为【业务编码+日期+3位自增序号】的唯一编号(例如:JJ20250826001)
 * 特性:基于Redis实现分布式自增,通过Redisson分布式锁保证并发安全,序号按日期重置
 */
@Slf4j // Lombok注解,自动注入日志对象log
@Component // Spring组件注解,将该类注册为Bean,交由Spring容器管理
public class CommonNumber {

    /**
     * Redis操作客户端(静态变量)
     * 用于执行自增、过期时间设置等Redis命令
     */
    private static BladeRedis BLADE_REDIS;

    /**
     * Redisson客户端(静态变量)
     * 用于获取分布式锁,保证多实例环境下的并发安全
     */
    private static RedissonClient REDISSSON_CLIENT;

    /**
     * Redisson客户端(实例变量)
     * 由Spring容器注入,通过@Resource注解按名称匹配
     */
    @Resource
    private RedissonClient RedissonClient;

    /**
     * Redis操作客户端(实例变量)
     * 由Spring容器注入,Blade框架封装的Redis客户端
     */
    @Resource
    private BladeRedis bladeRedis;

    /**
     * 初始化方法(PostConstruct注解)
     * 作用:在Spring Bean初始化完成后,将实例变量赋值给静态变量
     * 原因:工具方法为static,无法直接注入Spring Bean,通过该方式间接获取容器中的Bean实例
     */
    @PostConstruct
    public void init() {
        BLADE_REDIS = bladeRedis;
        REDISSSON_CLIENT = RedissonClient;
    }

    /**
     * 生成通用唯一编号
     * 格式:业务编码(code) + 年月日(yyyyMMdd) + 3位自增序号(不足补0)
     * 示例:code=JJ → JJ20250826001、JJ20250826002...
     *
     * @param code 业务编码(区分不同业务场景的编号前缀)
     * @return 格式化后的唯一编号
     * @throws ServiceException 当获取分布式锁失败或Redis操作异常时抛出
     */
    public static String getCommonNumber(String code) {
        // 1. 定义分布式锁key:按业务编码区分,避免不同业务锁竞争
        RLock lock = REDISSSON_CLIENT.getLock("common-number-lock:" + code);

        try {
            // 2. 尝试获取分布式锁:最多等待3秒,持有锁5秒(防止死锁)
            // tryLock返回false表示获取锁失败(并发过高)
            if (!lock.tryLock(3, 5, TimeUnit.SECONDS)) {
                throw new ServiceException("系统繁忙,请稍后重试");
            }

            // 3. 格式化当前日期为yyyyMMdd格式(用于序号按日期重置)
            String currentTime = DateUtils.format(new Date(), "yyyyMMdd");

            // 4. 定义Redis自增key:业务编码+日期,确保每天的序号从1开始
            String companyNumberKey = "common_number_key:" + code + currentTime;

            // 5. Redis自增操作:原子性递增,保证序号唯一(初始值为1,每次+1)
            Long incr = BLADE_REDIS.incr(companyNumberKey);

            // 6. 拼接最终编号:业务编码 + 日期 + 3位补0序号(例如:1→001,10→010,100→100)
            String companyNumber = code + currentTime + String.format("%03d", incr);

            // 7. 设置Rediskey过期时间:48小时(确保过期数据自动清理,节省Redis空间)
            BLADE_REDIS.expire(companyNumberKey, 60 * 60 * 48L);

            // 8. 返回生成的编号
            return companyNumber;

        } catch (InterruptedException e) {
            // 捕获线程中断异常(获取锁过程中线程被中断)
            log.error("获取编号时线程被中断,code:{}", code, e);
            throw new ServiceException("获取编号失败");
        } catch (ServiceException e) {
            // 抛出获取锁失败的自定义异常(无需额外日志,已在抛出时明确)
            throw e;
        } catch (Exception e) {
            // 捕获其他异常(Redis操作失败等)
            log.error("获取编号失败,code:{}", code, e);
            throw new ServiceException("获取编号失败");
        } finally {
            // 9. 释放分布式锁:必须在finally中执行,确保锁一定会释放
            // 先判断当前线程是否持有锁,避免释放其他线程的锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

```java

这是一个 基于Redis实现的分布式唯一编号生成工具类,核心功能是生成格式为 业务编码+日期+3位自增序号(如 JJ20250826001)的全局唯一编号,适用于分布式系统中需要有序、不重复编号的场景(如订单号、单据号等)。以下是详细解析:

一、类结构与依赖说明

1. 核心注解

2. 依赖组件

二、核心逻辑:编号生成流程

1. 方法定义

public static String getCommonNumber(String code)

2. 关键步骤(带并发安全保障)

(1)分布式锁获取

RLock lock = REDISSSON_CLIENT.getLock("common-number-lock:"+code);
if (!lock.tryLock(3, 5, TimeUnit.SECONDS)) {
    throw new ServiceException("系统繁忙,请稍后重试");
}

(2)Redis自增生成序号

// 1. 生成当天日期(格式:yyyyMMdd)
String currentTime = DateUtil.format(DateUtil.date(), "yyyyMMdd");
// 2. 构建Redis自增key(业务编码+日期,确保每天的序号独立重置)
String companyNumberKey = "common_number_key:" + code + currentTime;
// 3. Redis自增(原子操作,确保序号唯一)
Long incr = BLADE_REDIS.incr(companyNumberKey);
// 4. 序号补零(3位,不足3位前面补0,如1→001,10→010)
String seq = String.format("%03d", incr);

(3)设置Redis key过期时间

BLADE_REDIS.expire(companyNumberKey, 60 * 60 * 48L); // 48小时过期

(4)组装最终编号

String companyNumber = code + currentTime + seq;

3. 异常处理与锁释放

try {
    // 编号生成逻辑...
} catch (Exception e) {
    log.error("获取编号失败", e); // 打印异常堆栈,便于排查
    throw new ServiceException("获取编号失败"); // 抛出自定义业务异常,上层处理
} finally {
    // 确保锁一定释放(避免死锁)
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

三、核心设计亮点

1. 分布式并发安全

2. 序号合理性

3. 资源优化

四、潜在问题与优化建议

1. 潜在问题

2. 优化建议

五、使用场景

适用于分布式系统中需要生成 有序、唯一、可读 编号的场景,例如:

总结

该工具类基于Redis的原子自增和Redisson的分布式锁,实现了分布式环境下的唯一编号生成,设计简洁、实用性强,同时也存在一些可优化的细节(如锁策略、降级方案),可根据实际业务场景调整。

以上就是基于Redis实现的分布式唯一编号生成工具类的详细内容,更多关于Redis分布式唯一编号生成工具类的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文