数据库id生成方案-雪花算法(Snowflake ID)

181 阅读5分钟

类名

SnowflakeIdUtils —— 全局唯一ID生成工具类


功能概览

  1. 长整型雪花ID

    • 方法:nextId()

    • 返回类型:long

    • 特点:基于 Twitter 的 Snowflake 算法生成 64 位全局唯一ID

    • 组成结构:

      1bit 符号位 + 41bit 时间戳 + 10bit 机器ID + 12bit 序列号
      
    • 适用场景:数据库主键、分布式唯一标识

  2. 数字字符串形式的雪花ID

    • 方法:nextIdStr()
    • 返回类型:String
    • 特点:将 long 类型雪花ID 转为字符串,方便前端展示或传输
    • 适用场景:接口返回 ID、JSON 数据字段
  3. 带日期前缀的雪花ID

    • 方法:nextIdWithDate()
    • 返回类型:String
    • 格式:YYYYMMDD_雪花ID
    • 特点:增加日期前缀,便于按日期分组或归档
    • 适用场景:日志编号、报表编号、每日流水号
  4. 自定义前缀 + 日期 + 雪花ID

    • 方法:nextIdWithPrefix(String prefix)
    • 返回类型:String
    • 格式:前缀_YYYYMMDD_雪花ID
    • 特点:可添加业务前缀(如 "QR"、"FILE"、"ORD"),兼顾业务标识和唯一性
    • 适用场景:文件名、订单号、二维码编号
  5. 秒级时间戳 + 随机后缀ID

    • 方法:nextIdWithRandom()
    • 返回类型:String
    • 格式:yyyyMMddHHmmss + 4位随机数
    • 特点:适合对唯一性要求较低但要求短ID、可读性强的场景
    • 适用场景:文件名、二维码、短期唯一标识

核心设计特点

  1. 线程安全

    • 使用 synchronized 确保雪花ID生成器在高并发环境下不会重复生成ID
  2. 支持自定义机器ID

    • 方法:setMachineId(long id)
    • 范围:0~1023
    • 适用于多节点分布式环境,保证不同机器生成的ID不会冲突
  3. 雪花算法特性

    • 高性能:可在毫秒级别生成多条唯一ID
    • 分布式友好:机器ID + 时间戳 + 序列号
    • 可排序:生成的ID按时间递增
  4. 易用性

    • 提供多种生成方法满足不同业务需求
    • 返回值类型灵活(long / String
    • 可带日期或自定义前缀,便于业务追踪和管理

使用示例

package com.aj.parkofficesystem.utils;

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

/**
 * 全局雪花ID工具类 + 秒级时间戳 + 随机后缀ID
 *
 * 功能:
 * 1. long 类型雪花ID
 * 2. 数字字符串雪花ID
 * 3. 带日期前缀的雪花ID(YYYYMMDD_数字ID)
 * 4. 自定义前缀 + 日期 + 雪花ID
 * 5. 秒级时间戳 + 随机后缀ID(适合文件名、二维码等)
 *
 * 使用方式:
 * SnowflakeIdUtils.nextId();           // long 类型
 * SnowflakeIdUtils.nextIdStr();        // 数字字符串
 * SnowflakeIdUtils.nextIdWithDate();   // 日期前缀
 * SnowflakeIdUtils.nextIdWithPrefix("QR"); // 自定义前缀
 * SnowflakeIdUtils.nextIdWithRandom(); // 秒级时间戳 + 随机后缀
 */
public class SnowflakeIdUtils {

    // ============================== 基础配置 ==============================

    /** 起始时间戳(自定义纪元)2023-01-01 00:00:00 */
    private static final long START_STAMP = 1672531200000L;

    /** 序列号占用位数(12位) */
    private static final long SEQUENCE_BIT = 12;
    /** 机器ID占用位数(10位) */
    private static final long MACHINE_BIT = 10;

    /** 最大机器ID值(0~1023) */
    private static final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    /** 最大序列号值(0~4095) */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    /** 机器ID左移偏移量 */
    private static final long MACHINE_LEFT = SEQUENCE_BIT;
    /** 时间戳左移偏移量 */
    private static final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;

    /** 默认机器ID(可根据服务实例设置) */
    private static final long DEFAULT_MACHINE_ID = 1L;

    // ============================== 运行状态 ==============================

    /** 当前机器ID */
    private static long machineId = DEFAULT_MACHINE_ID;
    /** 当前毫秒内序列号 */
    private static long sequence = 0L;
    /** 上一次生成ID的时间戳 */
    private static long lastStamp = -1L;

    /** 禁止实例化 */
    private SnowflakeIdUtils() {}

    // ============================== 核心方法 ==============================

    /**
     * 设置机器ID(0~1023)
     * @param id 机器ID
     */
    public static void setMachineId(long id) {
        if (id < 0 || id > MAX_MACHINE_NUM) {
            throw new IllegalArgumentException("机器ID超出范围(0~1023)");
        }
        machineId = id;
    }

    /**
     * 获取下一个全局唯一ID(long 类型)
     * @return long 类型ID
     */
    public synchronized static long nextId() {
        long currStamp = System.currentTimeMillis();

        // 时钟回拨检测
        if (currStamp < lastStamp) {
            throw new RuntimeException("时钟回拨,拒绝生成ID");
        }

        // 同一毫秒内生成多个ID
        if (currStamp == lastStamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 当前毫秒序列用完,等待下一毫秒
            if (sequence == 0L) {
                currStamp = waitNextMillis(currStamp);
            }
        } else {
            // 新的毫秒,序列号从0开始
            sequence = 0L;
        }

        lastStamp = currStamp;

        // 组合ID:时间戳部分 + 机器ID部分 + 序列号部分
        return (currStamp - START_STAMP) << TIMESTAMP_LEFT
                | (machineId << MACHINE_LEFT)
                | sequence;
    }

    /**
     * 获取下一个全局唯一ID(数字字符串形式)
     * @return String 类型ID
     */
    public static String nextIdStr() {
        return String.valueOf(nextId());
    }

    /**
     * 获取带日期前缀的全局唯一ID(YYYYMMDD_数字ID)
     * @return 日期前缀ID
     */
    public static String nextIdWithDate() {
        String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
        return date + "_" + nextId();
    }

    /**
     * 获取自定义前缀 + 日期 + 数字ID
     * 例如:QR_20251027_4523367890123456789
     * @param prefix 自定义前缀(如 "QR", "FILE", "ORD")
     * @return 带前缀ID
     */
    public static String nextIdWithPrefix(String prefix) {
        String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
        return prefix + "_" + date + "_" + nextId();
    }

    /**
     * 获取秒级时间戳 + 随机后缀ID
     * 格式:yyyyMMddHHmmss + 4位随机数
     * 适合文件名、二维码等
     * @return 秒级时间戳 + 随机后缀ID
     */
    public static String nextIdWithRandom() {
        // 当前时间,精确到秒
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        // 随机数(1000~9999)
        int random = ThreadLocalRandom.current().nextInt(1000, 10000);
        return timestamp + random;
    }

    /**
     * 等待下一毫秒
     * @param currStamp 当前时间戳
     * @return 下一毫秒时间戳
     */
    private static long waitNextMillis(long currStamp) {
        while (currStamp <= lastStamp) {
            currStamp = System.currentTimeMillis();
        }
        return currStamp;
    }
}
public class Demo {
    public static void main(String[] args) {
        // 设置机器ID(可选)
        SnowflakeIdUtils.setMachineId(2);

        // long 类型雪花ID
        long id1 = SnowflakeIdUtils.nextId();

        // 数字字符串形式
        String id2 = SnowflakeIdUtils.nextIdStr();

        // 日期前缀ID
        String id3 = SnowflakeIdUtils.nextIdWithDate();

        // 自定义前缀ID
        String id4 = SnowflakeIdUtils.nextIdWithPrefix("QR");

        // 秒级时间戳 + 随机数ID
        String id5 = SnowflakeIdUtils.nextIdWithRandom();

        System.out.println(id1);
        System.out.println(id2);
        System.out.println(id3);
        System.out.println(id4);
        System.out.println(id5);
    }
}

总结

SnowflakeIdUtils 是一个 全能型 ID 工具类,将雪花ID和秒级随机ID统一封装。 提供多种生成方式,覆盖数据库主键、文件名、二维码、报表编号等常见业务场景。 线程安全、分布式可用、易于扩展。 可轻松在项目中直接调用,不需要额外依赖。