电商订单id设计思路

文章目录

  • 前言
  • 一、核心思路
  • [二、Snowflake 算法实现(Java版)](#二、Snowflake 算法实现(Java版))
      • [1️⃣ Snowflake 工具类](#1️⃣ Snowflake 工具类)
  • [三、订单号生成 Service](#三、订单号生成 Service)
      • [2️⃣ 订单号生成器](#2️⃣ 订单号生成器)
  • [四、Controller 测试](#四、Controller 测试)
  • 五、生产环境必须注意(重点)
      • [✅ 1. machineId / datacenterId 不能乱写](#✅ 1. machineId / datacenterId 不能乱写)
      • [✅ 2. 时钟回拨问题](#✅ 2. 时钟回拨问题)
      • [✅ 3. 高并发优化](#✅ 3. 高并发优化)
      • [✅ 4. 是否需要"用户可读订单号"](#✅ 4. 是否需要“用户可读订单号”)
  • 六、总结(给你决策建议)

前言

在 Spring Boot 里实现「业务前缀 + Snowflake ID


一、核心思路

订单号结构:

js 复制代码
订单号 = 业务前缀 + Snowflake生成的ID

比如:

js 复制代码
OD + 1789237498127349823

或者更好一点(带时间可读):

js 复制代码
OD + yyyyMMdd + SnowflakeID

二、Snowflake 算法实现(Java版)

1️⃣ Snowflake 工具类

java 复制代码
@Component
public class SnowflakeIdGenerator {

    // 起始时间戳(自己定义)
    private final long START_TIMESTAMP = 1700000000000L;

    // 每部分占用位数
    private final long SEQUENCE_BIT = 12;   // 序列号
    private final long MACHINE_BIT = 5;     // 机器ID
    private final long DATACENTER_BIT = 5;  // 机房ID

    // 最大值
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    private final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);

    // 位移
    private final long MACHINE_LEFT = SEQUENCE_BIT;
    private final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;
    private long machineId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator() {
        this(1, 1); // 默认机房ID=1,机器ID=1
    }

    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId非法");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId非法");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long currTimestamp = System.currentTimeMillis();

        if (currTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨,拒绝生成ID");
        }

        if (currTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                currTimestamp = waitNextMillis();
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = currTimestamp;

        return (currTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT
                | datacenterId << DATACENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }

    private long waitNextMillis() {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

三、订单号生成 Service

2️⃣ 订单号生成器

java 复制代码
@Service
public class OrderNoGenerator {

    @Autowired
    private SnowflakeIdGenerator snowflakeIdGenerator;

    public String generateOrderNo() {
        long id = snowflakeIdGenerator.nextId();

        String date = LocalDate.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        return "OD" + date + id;
    }
}

四、Controller 测试

java 复制代码
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderNoGenerator orderNoGenerator;

    @GetMapping("/gen")
    public String genOrderNo() {
        return orderNoGenerator.generateOrderNo();
    }
}

访问:

js 复制代码
GET /order/gen

返回示例:

js 复制代码
OD202604271789237498127349823

五、生产环境必须注意(重点)

✅ 1. machineId / datacenterId 不能乱写

否则会重复

推荐:

  • 从配置中心获取
  • 或用 IP hash
  • 或用容器编号

✅ 2. 时钟回拨问题

解决方案:

  • NTP 时间同步
  • 或检测后等待(上面已处理)

✅ 3. 高并发优化

当前是 synchronized,可以:

  • 换成:

    • AtomicLong + CAS
    • 或直接用开源方案:

👉 更推荐:

  • 美团 Leaf
  • 百度 UidGenerator

✅ 4. 是否需要"用户可读订单号"

如果你要给用户看,建议:

js 复制代码
OD + 日期 + 随机码/短ID

否则 Snowflake 太长:

js 复制代码
OD202604271789237498127349823

可以优化成:

js 复制代码
OD2404278F3K92

六、总结(给你决策建议)

场景 推荐
小项目 时间戳 + Redis INCR
中大型电商 Snowflake
超高并发(亿级) Leaf / UidGenerator

后续可以升级成:

✅ 支持分库分表

✅ 支持多机房自动分配ID

✅ 可读订单号(防爬虫、防推测)

✅ 短ID版本(类似抖音订单号)

相关推荐
0xDevNull7 小时前
Spring注解@Requestbody、@Requestparam、@PathVariable
java·后端·spring
budingxiaomoli7 小时前
环境和工程创建
java·spring·springcloud
梦想不只是梦与想7 小时前
java中多态的属性和方法
java·多态属性·多态方法
CN-Dust7 小时前
【C++】for循环嵌套例题专题
java·c++·算法
speop7 小时前
Reasoning kingdom chapter13
android·java·python
让我上个超影吧8 小时前
【MYSQL】索引下推
java·数据库·mysql
QuZero8 小时前
ReentrantReadWriteLock mechanism
java·后端·算法
超级无敌葛大侠8 小时前
Redis里RDB和AOF的区别
java·redis
YJlio8 小时前
《Windows Internals》10.5.1 ETW 概述:看懂 Windows 的“事件高速公路”
java·windows·笔记·stm32·嵌入式硬件·学习·eclipse