我们是由枫哥组建的IT技术团队成立于2017年致力于帮助IT从业者提供实力成功入职理想企业我们提供一对一学习辅导由知名大厂导师指导分享Java技术、参与项目实战等服务并为学员定制职业规划全面提升竞争力过去8年我们已成功帮助数千名求职者拿到满意的OfferIT枫斗者、IT枫斗者-Java面试突击。简介项目中订单号、流水号、业务编码随处可见很多人只会照搬 UUID、雪花算法、Leaf 等开源轮子但通用组件无法适配自定义业务前缀、时间格式、并发规则。本文从零拆解三套自研ID方案从单机零依赖到分布式高并发层层迭代可直接落地生产。 前言为什么要自研唯一ID生成器日常开发中绝大多数开发者生成唯一编号只会用现成方案UUID简单无依赖但无序、过长、数据库索引效率极低雪花算法有序高性能但固定位分配无法自定义业务前缀美团Leaf/滴滴Tinyid功能强大但引入第三方组件重架构、部署复杂。在很多轻量化业务场景中我们需要带业务标识、带时间格式、可自定义规则、轻量无侵入的唯一编码。通用轮子无法满足个性化需求自研ID生成方案才是最优解。本文循序渐进从单机零依赖方案 → 分布式Redis号段方案 → 企业级自定义业务方案完整拆解自研ID的设计思路、源码实现、优缺点及场景选型看完即可手搓适配自己项目的唯一ID工具一、唯一ID设计核心四大核心权衡一个优秀的唯一编号生成规则必须平衡唯一性、有序性、高性能、存储效率四大核心指标。业界所有ID方案本质都是三种设计思路的取舍1.1 中心化强一致Redis/数据库依托中间件原子能力发号严格保证全局唯一、递增有序。通过号段预取解决中间件性能瓶颈适合高并发分布式场景唯一缺点是可能出现跳号。1.2 去中心化组合式雪花算法本地拼接「时间戳机器ID自增序列」无网络开销、性能极致趋势递增。但强依赖系统时钟存在时钟回拨风险规则固定无法自定义。1.3 概率型UUID纯随机生成零协调、零依赖但完全无序、字符串过长严重影响数据库索引性能仅适用于无排序、无存储优化需求的场景。设计本质在「本地无协调高性能」和「中心协调强唯一」之间做平衡通过位分配、号段预取、定时过期打破性能与唯一性的僵局。二、单机版自研ID零依赖、纯内存、宏观有序单机方案是所有分布式方案的基础核心优势是无需任何中间件、纯JVM内存操作、性能拉满适合单体项目、低并发后台服务。2.1 设计思想不依赖Redis、数据库等外部组件全程JVM内生成时间戳左移占位保证ID宏观时间有序PID随机数混合生成进程指纹区分不同进程AtomicLong 线程自增保证单进程线程安全位运算拼接精简长度、提升生成效率。2.2 完整可运行源码importjava.lang.management.ManagementFactory;importjava.util.concurrent.ThreadLocalRandom;importjava.util.concurrent.atomic.AtomicLong;/** * 单机版唯一ID生成工具零外部依赖、纯内存 */publicfinalclassUniqueIdUtils{// 自增序列保证同一毫秒内唯一privatestaticfinalAtomicLongSEQnewAtomicLong(0);// 时间戳左移20位低位20位留给进程指纹自增序列privatestaticfinallongTIME_SHIFT20L;// 进程唯一指纹区分不同进程避免多进程冲突privatestaticfinallongPROCESS_SEED;static{// 获取JVM进程信息格式 PID主机名StringrunNameManagementFactory.getRuntimeMXBean().getName();longpid0L;if(runName.contains()){pidLong.parseLong(runName.split()[0]);}// 生成20位随机数最大 1048575longrandomThreadLocalRandom.current().nextLong(1L20);// PID异或随机数混合进程特征保留低20位PROCESS_SEED(pid^random)((1L20)-1);}// 私有构造禁止实例化privateUniqueIdUtils(){}/** * 生成Long型唯一ID */publicstaticlongnextId(){// 毫秒时间戳高位占位longtimePartSystem.currentTimeMillis()TIME_SHIFT;// 自增序列取低20位防止溢出longseqPartSEQ.incrementAndGet()((1L20)-1);// 位运算拼接时间戳 | 进程指纹 | 自增序列returntimePart|PROCESS_SEED|seqPart;}/** * 生成字符串ID便于日志、接口传输、存储 */publicstaticStringnextIdStr(){returnLong.toUnsignedString(nextId());}// 测试publicstaticvoidmain(String[]args){System.out.println(nextIdStr());System.out.println(nextIdStr());}}2.3 方案优缺点总结✅ 优点零外部依赖无需Redis、数据库开箱即用纯内存位运算生成性能极致高基于时间戳生成宏观趋势有序索引友好。❌ 缺点多机器、多进程同PID场景理论存在极小概率冲突服务重启后序列重置无状态无法追溯不支持分布式集群环境仅适用于单体服务。三、分布式高并发Redis号段预取ID方案单机方案无法适配集群部署针对分布式高并发场景采用Redis中心化发号 本地号段缓存方案完美平衡唯一性与高性能。3.1 设计思想利用Redis单线程模型INCR/INCRBY保证全局自增原子性引入号段预取机制一次性从Redis申请批量序列号缓存到本地常规请求直接读取本地内存号段无网络开销彻底解决Redis并发瓶颈号段耗尽后自动续批保证全局唯一不重复。3.2 完整实战源码核心工具类importorg.springframework.data.redis.core.StringRedisTemplate;/** * Redis号段模式 分布式唯一ID生成器 * 高性能本地缓存号段极少请求Redis */publicclassRedisSegmentSequence{privatefinalStringRedisTemplateredisTemplate;privatefinalStringredisKey;privatefinallongstep;// 本地号段最大值privatevolatilelonglocalMax-1;// 本地自增序列privatefinalAtomicLonglocalSeqnewAtomicLong(-1);publicRedisSegmentSequence(StringRedisTemplateredisTemplate,StringredisKey,longstep){this.redisTemplateredisTemplate;this.redisKeyredisKey;this.stepstep;}/** * 获取全局唯一ID */publicsynchronizedlongnextId(){longcurrentlocalSeq.incrementAndGet();// 本地号段耗尽重新拉取号段if(currentlocalMax){fetchNextSegment();currentlocalSeq.incrementAndGet();}returncurrent;}/** * 从Redis批量拉取号段 */privatevoidfetchNextSegment(){LongmaxNumredisTemplate.opsForValue().increment(redisKey,step);if(maxNumnull){thrownewIllegalStateException(Redis号段获取失败请检查Redis连接);}this.localMaxmaxNum;// 本地起始位置当前最大值-步长this.localSeq.set(maxNum-step);}}Spring容器配置直接注入使用importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.core.StringRedisTemplate;/** * ID生成器配置类 * 不同业务可配置不同key隔离号段 */ConfigurationpublicclassSequenceConfig{BeanpublicRedisSegmentSequenceorderSequence(StringRedisTemplateredisTemplate){// 订单号每次预取1000个号段适配高并发returnnewRedisSegmentSequence(redisTemplate,seq:order,1000);}BeanpublicRedisSegmentSequencepaySequence(StringRedisTemplateredisTemplate){// 支付单号独立号段业务隔离returnnewRedisSegmentSequence(redisTemplate,seq:pay,1000);}}3.3 方案优缺点与优化✅ 优点绝大多数请求走本地内存无网络RTT并发性能拉满Redis原子发号全局绝对唯一无重复风险业务号段隔离订单、支付、流水号互不干扰。❌ 缺点服务重启、宕机时本地未用完号段会丢失出现跳号 生产优化跳号不影响任何业务绝大多数业务无需连续编号只需唯一即可。低并发场景可缩小step步长减少号段浪费极致精准场景可取消号段直接INCR逐条生成。四、企业级实战自定义业务流水号线上落地方案前面两种方案仅能生成纯数字ID无法携带业务信息。生产中最常用的是「业务前缀时间戳秒级自增序列」自定义流水号可读性极强、方便问题追溯。4.1 业务编号规则示例格式UG20260521153035001UG2位自定义业务码区分订单、用户、支付、日志模块2026052115303514位秒级时间戳精准到秒方便追溯生成时间0013位自增序列每秒最大支持999个并发4.2 核心设计亮点通过Lua脚本保证「自增过期」原子执行杜绝并发重复按秒维度生成RedisKey自动过期不占用Redis内存序列位数可自定义适配不同并发量级带业务标识时间可读性远超雪花算法、UUID。4.3 线上可用完整源码importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.core.script.DefaultRedisScript;importorg.springframework.data.redis.core.script.RedisScript;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;importjava.util.Collections;/** * 自定义业务流水号生成器企业线上落地版 * 规则业务码 年月日时分秒 3位自增序列 */ComponentpublicclassSerialNumberGenerator{ResourceprivateStringRedisTemplateredisTemplate;// Lua脚本原子自增 首次设置过期时间privatestaticfinalStringLUA_SCRIPTlocal current redis.call(incr, KEYS[1]) if current 1 then redis.call(expire, KEYS[1], ARGV[1]) end return current;privatestaticfinalRedisScriptLongREDIS_SCRIPTnewDefaultRedisScript(LUA_SCRIPT,Long.class);privatestaticfinalDateTimeFormatterTIME_FORMATDateTimeFormatter.ofPattern(yyyyMMddHHmmss);/** * 生成业务流水号 * param bizCode 业务前缀编码 * return 唯一业务编号 */publicStringgenerate(StringbizCode){LocalDateTimenowLocalDateTime.now();StringtimeStrnow.format(TIME_FORMAT);// 按【业务秒】维度拼接keyStringredisKeyString.format(seq:%s:%s,bizCode,timeStr);// 原子执行脚本过期时间设置2秒兜底防key常驻LongsequenceredisTemplate.execute(REDIS_SCRIPT,Collections.singletonList(redisKey),2);if(sequencenull||sequence999){thrownewRuntimeException(序列号生成失败或当前秒并发超限);}// 格式拼接不足3位补0returnbizCodetimeStrString.format(%03d,sequence);}}五、三套方案场景选型总结看完三套方案直接根据业务场景对号入座即可方案类型核心优势适用场景单机零依赖ID无中间件、性能极高、极简单体项目、低并发后台、临时流水号Redis号段ID分布式唯一、高并发、低Redis压力集群部署、高并发订单、支付ID自定义业务流水号可读性强、带业务时间、可追溯业务单据、日志编号、可追溯流水号六、全文总结不要盲目依赖UUID、雪花算法等通用轮子真正适配业务的ID一定是自研定制的。从单机零依赖到分布式高并发三套方案层层迭代覆盖99%的项目唯一编号场景简单单体项目用单机内存ID零依赖最省事分布式高并发场景用Redis号段预取方案性能与唯一性兼顾需要业务追溯、可读性编号用自定义秒级自增流水号企业级最优解。⭐️推荐:Offer训练营介绍Java 面试 后端通用面试八股文Java后端企业级实战面试Java后端校招算法学习