雪花 ID 分布式唯一主键生成

摘要

代拍系统订单、商品、议价、包裹、消息等海量数据表,单机自增 ID 存在分库分表冲突、泄露业务量级问题。本文采用雪花算法生成全局唯一 64 位数字 ID,拆分机器、业务、时间分段,适配分布式多节点部署,附带工具类完整代码,bidfans 所有业务主键均基于该算法生成。

一、自增 ID 分布式缺陷

单库自增 ID 在多服务、分库场景下会出现主键重复;数字连续自增易被外部统计平台订单、商品总量,存在业务数据泄露风险;ID 无业务分段标识,无法从主键快速区分数据所属业务表,检索排查效率低。 雪花 ID 内置时间戳、机器编号、业务标识三段信息,全局不重复,无连续规律,同时可通过 ID 解析生成时间、业务类型,便于日志排查。

二、雪花 ID 分段规则设计

64 位二进制分段:1 位符号位固定 0;41 位毫秒时间戳;5 位业务标识位(区分订单 / 商品 / 包裹);8 位机器节点编号;9 位自增序列号。 41 位时间戳可使用数十年,5 位支持 32 类业务区分,8 位支持 256 台服务节点,9 位每毫秒生成 512 个 ID,完全满足代拍系统并发创建数据需求。

三、雪花生成工具代码

复制代码
public class SnowIdGenerator {
    // 时间戳起始基准
    private static final long START_TIME = 1704067200000L;
    private static final int BIZ_BIT = 5;
    private static final int WORK_BIT = 8;
    private static final int SEQ_BIT = 9;
    private final long workId;
    private final long bizType;
    private long lastTime = System.currentTimeMillis();
    private long sequence = 0;

    public SnowIdGenerator(long workId, long bizType) {
        this.workId = workId;
        this.bizType = bizType;
    }
    public synchronized long nextId() {
        long now = System.currentTimeMillis();
        if (now < lastTime) throw new RuntimeException("时钟回拨,禁止生成ID");
        if (now == lastTime) {
            sequence = (sequence + 1) & ((1 << SEQ_BIT) - 1);
            if (sequence == 0) now = waitNextMs(lastTime);
        } else sequence = 0;
        lastTime = now;
        long timePart = (now - START_TIME) << (BIZ_BIT + WORK_BIT + SEQ_BIT);
        long bizPart = bizType << (WORK_BIT + SEQ_BIT);
        long workPart = workId << SEQ_BIT;
        return timePart | bizPart | workPart | sequence;
    }
    private long waitNextMs(long last) {
        long now;
        do { now = System.currentTimeMillis(); } while (now <= last);
        return now;
    }
}

启动服务时读取配置文件分配机器编号,不同业务创建独立生成器实例,保证 ID 携带业务标识。

四、时钟回拨容错处理

机器时钟回调会造成 ID 重复,工具类内置时间校验,检测当前时间小于上次生成时间直接抛出异常,阻断 ID 生成,同时记录告警日志,运维及时校准服务器时间。bidfans 多节点集群部署三年,未出现主键重复问题。

结语

定制化雪花 ID 生成器解决分布式主键冲突、数据泄露问题,内置业务分段便于运维排查,轻量无第三方依赖,适配所有分布式跨境代拍系统主键生成需求。