searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Spring Boot 实现订单号生成逻辑

2024-10-12 09:45:03
1
0

几种常见的解决方案

  1. UUID 实例代码

  2. 数据库序列或自增ID

  3. 时间戳 + 随机数/序列

  4. 分布式唯一ID生成方案

    • Snowflake ID结构

    • 类定义和变量初始化

    • 构造函数

    • ID生成方法

    • 辅助方法

在 Spring Boot 中设计一个订单号生成系统时,需考虑生成的订单号的唯一性、可扩展性及业务相关性。以下是几种常见的解决方案及相应的示例代码:

1. UUID

使用 UUID 生成唯一的订单号,形式为 8-4-4-4-12 的字符串,例如 123e4567-e89b-12d3-a456-426614174000。优点是简单,缺点是较长且不易记忆。

实例代码

import java.util.UUID;

public class UUIDGenerator {
public static String generateUUID() {
return UUID.randomUUID().toString();
}

public static void main(String[] args) {
System.out.println("Generated UUID: " + generateUUID());
}
}



2. 数据库序列或自增ID

利用数据库的序列或自增ID生成唯一的订单号,常见于单体应用。

实例代码

 
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    // 其他属性
 }
 

数据库示例

  • PostgreSQL:

CREATE SEQUENCE order_id_seq START WITH 1 INCREMENT BY 1;
CREATE TABLE orders (order_id bigint NOT NULL DEFAULT nextval('order_id_seq'), order_data text);
  • MySQL:

CREATE TABLE orders (order_id INT AUTO_INCREMENT, order_data TEXT, PRIMARY KEY (order_id));

3. 时间戳 + 随机数/序列

结合时间戳与随机数生成订单号,增强可读性与业务相关性。

实例代码

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;

public class OrderNumberGenerator {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final int RANDOM_NUM_BOUND = 10000;

    public static String generateOrderNumber(String prefix) {
        String timestamp = dateFormat.format(new Date());
        int randomNumber = ThreadLocalRandom.current().nextInt(RANDOM_NUM_BOUND);
        return prefix + timestamp + String.format("%04d", randomNumber);
    }

    public static void main(String[] args) {
        System.out.println("Generated Order Number: " + generateOrderNumber("ORD"));
    }
}

4. 分布式唯一ID生成方案

使用 Snowflake 算法生成唯一 ID,其中包含时间戳、数据中心ID、机器ID和序列号,支持分布式系统中的 ID 唯一性和有序性。

Snowflake ID 结构

  • 1 位符号位

  • 41 位时间戳

  • 10 位数据中心 ID 和机器 ID

  • 12 位序列号

实现示例

public class SnowflakeIdGenerator {
    private long datacenterId; // 数据中心ID    
    private long machineId;    // 机器ID   
    private long sequence = 0L; // 序列号   
    private long lastTimestamp = -1L; // 上一次时间戳
    private final long twepoch = 1288834974657L;
    private final long datacenterIdBits = 5L;
    private final long machineIdBits = 5L;
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    private final long sequenceBits = 12L;
    private final long machineIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + machineIdBits;
    private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than %d or less than 0");
        }
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than %d or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (machineId << machineIdShift) | sequence;
    }

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

 

下面是对这段代码的逐行解释:

类定义和变量初始化

  • private long datacenterId; 定义数据中心ID。

  • private long machineId; 定义机器ID。

  • private long sequence = 0L; 序列号,用于同一毫秒内生成多个ID时区分这些ID。

  • private long lastTimestamp = -1L; 上一次生成ID的时间戳。

以下是Snowflake算法的一些关键参数:

  • private final long twepoch = 1288834974657L; 系统的起始时间戳,这里是Snowflake算法的作者选择的一个固定的时间点(2010-11-04 09:42:54.657 GMT)。

  • private final long datacenterIdBits = 5L; 数据中心ID所占的位数。

  • private final long machineIdBits = 5L; 机器ID所占的位数。

  • private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 数据中心ID的最大值,这里通过位运算计算得出。

  • private final long maxMachineId = -1L ^ (-1L << machineIdBits); 机器ID的最大值,同样通过位运算得出。

  • private final long sequenceBits = 12L; 序列号占用的位数。

以下是一些用于位运算的参数,用于计算最终的ID:

  • private final long machineIdShift = sequenceBits; 机器ID的偏移位数。

  • private final long datacenterIdShift = sequenceBits + machineIdBits; 数据中心ID的偏移位数。

  • private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits; 时间戳的偏移位数。

  • private final long sequenceMask = -1L ^ (-1L << sequenceBits); 用于保证序列号在指定范围内循环。

构造函数

构造函数SnowflakeIdGenerator(long datacenterId, long machineId)接收数据中心ID和机器ID作为参数,并对这些参数进行校验,确保它们在合法范围内。

ID生成方法

public synchronized long nextId()是生成ID的核心方法,使用synchronized保证线程安全。

  • 首先获取当前时间戳。

  • 如果当前时间戳小于上一次生成ID的时间戳,抛出异常,因为时钟回拨会导致ID重复。

  • 如果当前时间戳等于上一次的时间戳(即同一毫秒内),通过增加序列号生成不同的ID;如果序列号溢出(超过最大值),则等待到下一个毫秒。

  • 如果当前时间戳大于上一次的时间戳,重置序列号为0。

  • 最后,将时间戳、数据中心ID、机器ID和序列号按照各自的偏移量左移,然后进行位或运算,组合成一个64位的ID。

辅助方法

private long tilNextMillis(long lastTimestamp)是一个辅助方法,用于在序列号溢出时等待直到下一个毫秒。

 

 

 

0条评论
作者已关闭评论
孟****寅
44文章数
0粉丝数
孟****寅
44 文章 | 0 粉丝
原创

Spring Boot 实现订单号生成逻辑

2024-10-12 09:45:03
1
0

几种常见的解决方案

  1. UUID 实例代码

  2. 数据库序列或自增ID

  3. 时间戳 + 随机数/序列

  4. 分布式唯一ID生成方案

    • Snowflake ID结构

    • 类定义和变量初始化

    • 构造函数

    • ID生成方法

    • 辅助方法

在 Spring Boot 中设计一个订单号生成系统时,需考虑生成的订单号的唯一性、可扩展性及业务相关性。以下是几种常见的解决方案及相应的示例代码:

1. UUID

使用 UUID 生成唯一的订单号,形式为 8-4-4-4-12 的字符串,例如 123e4567-e89b-12d3-a456-426614174000。优点是简单,缺点是较长且不易记忆。

实例代码

import java.util.UUID;

public class UUIDGenerator {
public static String generateUUID() {
return UUID.randomUUID().toString();
}

public static void main(String[] args) {
System.out.println("Generated UUID: " + generateUUID());
}
}



2. 数据库序列或自增ID

利用数据库的序列或自增ID生成唯一的订单号,常见于单体应用。

实例代码

 
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    // 其他属性
 }
 

数据库示例

  • PostgreSQL:

CREATE SEQUENCE order_id_seq START WITH 1 INCREMENT BY 1;
CREATE TABLE orders (order_id bigint NOT NULL DEFAULT nextval('order_id_seq'), order_data text);
  • MySQL:

CREATE TABLE orders (order_id INT AUTO_INCREMENT, order_data TEXT, PRIMARY KEY (order_id));

3. 时间戳 + 随机数/序列

结合时间戳与随机数生成订单号,增强可读性与业务相关性。

实例代码

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;

public class OrderNumberGenerator {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final int RANDOM_NUM_BOUND = 10000;

    public static String generateOrderNumber(String prefix) {
        String timestamp = dateFormat.format(new Date());
        int randomNumber = ThreadLocalRandom.current().nextInt(RANDOM_NUM_BOUND);
        return prefix + timestamp + String.format("%04d", randomNumber);
    }

    public static void main(String[] args) {
        System.out.println("Generated Order Number: " + generateOrderNumber("ORD"));
    }
}

4. 分布式唯一ID生成方案

使用 Snowflake 算法生成唯一 ID,其中包含时间戳、数据中心ID、机器ID和序列号,支持分布式系统中的 ID 唯一性和有序性。

Snowflake ID 结构

  • 1 位符号位

  • 41 位时间戳

  • 10 位数据中心 ID 和机器 ID

  • 12 位序列号

实现示例

public class SnowflakeIdGenerator {
    private long datacenterId; // 数据中心ID    
    private long machineId;    // 机器ID   
    private long sequence = 0L; // 序列号   
    private long lastTimestamp = -1L; // 上一次时间戳
    private final long twepoch = 1288834974657L;
    private final long datacenterIdBits = 5L;
    private final long machineIdBits = 5L;
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    private final long sequenceBits = 12L;
    private final long machineIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + machineIdBits;
    private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than %d or less than 0");
        }
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than %d or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (machineId << machineIdShift) | sequence;
    }

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

 

下面是对这段代码的逐行解释:

类定义和变量初始化

  • private long datacenterId; 定义数据中心ID。

  • private long machineId; 定义机器ID。

  • private long sequence = 0L; 序列号,用于同一毫秒内生成多个ID时区分这些ID。

  • private long lastTimestamp = -1L; 上一次生成ID的时间戳。

以下是Snowflake算法的一些关键参数:

  • private final long twepoch = 1288834974657L; 系统的起始时间戳,这里是Snowflake算法的作者选择的一个固定的时间点(2010-11-04 09:42:54.657 GMT)。

  • private final long datacenterIdBits = 5L; 数据中心ID所占的位数。

  • private final long machineIdBits = 5L; 机器ID所占的位数。

  • private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 数据中心ID的最大值,这里通过位运算计算得出。

  • private final long maxMachineId = -1L ^ (-1L << machineIdBits); 机器ID的最大值,同样通过位运算得出。

  • private final long sequenceBits = 12L; 序列号占用的位数。

以下是一些用于位运算的参数,用于计算最终的ID:

  • private final long machineIdShift = sequenceBits; 机器ID的偏移位数。

  • private final long datacenterIdShift = sequenceBits + machineIdBits; 数据中心ID的偏移位数。

  • private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits; 时间戳的偏移位数。

  • private final long sequenceMask = -1L ^ (-1L << sequenceBits); 用于保证序列号在指定范围内循环。

构造函数

构造函数SnowflakeIdGenerator(long datacenterId, long machineId)接收数据中心ID和机器ID作为参数,并对这些参数进行校验,确保它们在合法范围内。

ID生成方法

public synchronized long nextId()是生成ID的核心方法,使用synchronized保证线程安全。

  • 首先获取当前时间戳。

  • 如果当前时间戳小于上一次生成ID的时间戳,抛出异常,因为时钟回拨会导致ID重复。

  • 如果当前时间戳等于上一次的时间戳(即同一毫秒内),通过增加序列号生成不同的ID;如果序列号溢出(超过最大值),则等待到下一个毫秒。

  • 如果当前时间戳大于上一次的时间戳,重置序列号为0。

  • 最后,将时间戳、数据中心ID、机器ID和序列号按照各自的偏移量左移,然后进行位或运算,组合成一个64位的ID。

辅助方法

private long tilNextMillis(long lastTimestamp)是一个辅助方法,用于在序列号溢出时等待直到下一个毫秒。

 

 

 

文章来自个人专栏
行业动态-mdy
44 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0