System.currentTimeMillis()需结合归一化时间戳、毫秒内序号和机器标识才能生成唯一序列号:减去自定义纪元时间压缩位数,用AtomicInteger防重,分布式下加入机器ID,推荐18位纯数字格式。

System.currentTimeMillis() 本身不能直接生成唯一序列号,它只是毫秒级时间戳,同一毫秒内多次调用必然重复。真正可用的方案,是把它作为结构化编号的“时间基底”,再叠加可控的补充因子——关键不在拼接,而在设计。
时间戳必须做归一化处理
直接使用原始时间戳(如 1749952800000)会导致编号过长、起始时间不可控、未来扩展性差。推荐减去一个自定义纪元时间(epoch),把数值压缩到合理范围:
- 选一个业务上线时间作为起点,比如
2024-01-01 00:00:00对应的毫秒值1704067200000L - 计算偏移量:
currentMs = System.currentTimeMillis() - START_EPOCH - 这样生成的数字更短(通常11~13位),也便于估算编号生命周期
同一毫秒内必须有防重机制
高并发场景下,单靠时间戳完全不够。需在毫秒粒度内引入轻量有序标识:
- 用
AtomicInteger维护每毫秒内的递增序号,最大值建议设为999(3位)或9999(4位) - 序号溢出时,可选择阻塞等待下一毫秒,或丢弃重试(视业务容忍度而定)
- 避免用
Math.random()或无状态随机数——它无法解决毫秒内冲突,只增加不确定性
跨进程/机器需加入标识字段
单机单JVM可用线程ID或简单机器码;分布式环境必须区分实例:
- 从配置读取固定 ID(如
"srv01"),或基于 IP 做哈希取模生成 2~4 位数字 - Java 9+ 可用
ProcessHandle.current().pid()获取进程号,截取后两位足够区分 - 不建议直接拼 UUID 全串——36 字符含横线和字母,不利于数据库索引、日志检索和前端展示
推荐组合格式与示例
一个平衡唯一性、可读性与存储效率的典型结构是:13位偏移时间戳 + 3位毫秒内序号 + 2位机器标识,共18位纯数字:
- 示例输出:
0012345678901_123_01(可去掉下划线得001234567890112301) - 时间部分可反解:加上
START_EPOCH就能还原成真实时间,便于问题排查 - 整个字符串支持直接存入
BIGINT或CHAR(18)字段,无需额外解析

















