NodeJs 学习日志(8):雪花算法生成唯一 ID

NodeJs 学习日志(8):雪花算法生成唯一 ID(utils/snowflake.js 实现)

在后端开发中,唯一 ID 生成 是高频需求(如用户 ID、订单 ID、日志 ID 等)。传统的自增 ID 存在分布式环境下冲突、安全性差(易暴露数据量)等问题,而 雪花算法(Snowflake) 生成的 64 位数字 ID,兼具有序性、唯一性、高性能等优点,成为分布式系统的首选方案。

一、雪花算法核心原理

雪花算法生成的 64 位 ID 结构如下(从高位到低位):

字段位数说明
符号位1 位固定为 0(确保 ID 为正数)
时间戳40 位从自定义起始时间戳到当前的毫秒数
机器 ID12 位区分不同服务器 / 进程(避免跨节点冲突)
序列号10 位同一毫秒内的自增序列(避免同节点并发冲突)
  • 唯一性保障:机器 ID 区分节点,序列号区分同一节点同一毫秒的请求,时间戳保证整体有序。
  • 性能:无网络依赖,本地生成。
  • 有序性:按时间戳递增,便于数据库索引优化。

二、snowflake实现

/**
 * 雪花算法工具类(单例模式)
 * 用于生成全局唯一的64位数字ID
 * 特性:多文件引入时共享同一实例,避免机器ID/序列号冲突
 */
const SnowflakeId = require('snowflake-id').default;

// 单例实例存储
let snowflakeInstance = null;

/**
 * 初始化雪花算法实例(单例模式)
 * @param {Object} options - 配置项(可选,默认使用以下配置)
 * @param {number} options.mid - 机器ID(0-4095,分布式环境需保证唯一)
 * @param {number} options.offset - 起始时间戳偏移(默认:2021-01-01 00:00:00 的时间戳)
 * @param {number} options.timeBits - 时间戳占用位数(默认40位,支持最大时间范围:(2^40)/365/24/3600/1000 ≈ 34年)
 * @param {number} options.machineBits - 机器ID占用位数(默认12位,支持最大4096个节点)
 * @param {number} options.seqBits - 序列号占用位数(默认10位,支持每毫秒1024个ID)
 * @returns {SnowflakeId} 雪花算法实例
 */
function initSnowflake(options = {}) {
    // 单例判断:已初始化则直接返回实例
    if (snowflakeInstance) {
        return snowflakeInstance;
    }

    // 默认配置(可根据业务需求调整)
    const defaultOptions = {
        mid: 1, // 本地开发默认机器ID=1,生产环境需按节点分配(如从环境变量读取)
        offset: (2021 - 1970) * 31536000 * 1000, // 起始时间:2021-01-01 00:00:00
        timeBits: 40,
        machineBits: 12,
        seqBits: 10
    };

    // 合并配置(用户传入配置覆盖默认配置)
    const finalOptions = { ...defaultOptions, ...options };

    // 验证机器ID合法性(必须小于 2^machineBits - 1)
    const maxMachineId = (1 << finalOptions.machineBits) - 1;
    if (finalOptions.mid < 0 || finalOptions.mid > maxMachineId) {
        throw new Error(`机器ID必须在 0-${maxMachineId} 之间(当前配置machineBits=${finalOptions.machineBits}`);
    }

    // 初始化实例并缓存
    snowflakeInstance = new SnowflakeId(finalOptions);
    console.log('雪花算法实例初始化成功', {
        machineId: finalOptions.mid,
        startTime: new Date(finalOptions.offset).toLocaleString(),
        maxMachineCount: maxMachineId + 1,
        maxSeqPerMs: (1 << finalOptions.seqBits)
    });

    return snowflakeInstance;
}

/**
 * 生成唯一ID(核心方法)
 * @param {Object} options - 初始化配置(仅首次调用时生效)
 * @returns {string} 雪花ID(字符串格式,避免大数字精度丢失)
 */
function generateSnowflakeId(options = {}) {
    const instance = initSnowflake(options);
    return instance.generate();
}

// 导出单例方法和实例化方法
module.exports = {
    initSnowflake,
    generateSnowflakeId
};

三、使用示例,在多文件中使用

  1. 安装依赖
    首先安装 snowflake-id 库:
npm install snowflake-id
  1. 项目目录结构(示例)
project-name/
├── utils/
│   └── snowflake.js  # 雪花算法工具类
├── service/
│   ├── userService.js  # 用户服务(生成用户ID)
└── app.js  # 入口文件
  1. 多文件中使用(共享同一实例)
    (1)入口文件app.js(提前初始化配置)
const { initSnowflake } = require('./utils/snowflake');

// 项目启动时初始化(指定机器ID=2,覆盖默认配置)
// 生产环境建议从环境变量读取机器ID:process.env.MACHINE_ID
initSnowflake({
    mid: 2 // 分布式环境中,每个节点需配置唯一的machineId(0-4095)
});

console.log('项目启动成功,雪花算法实例已初始化');

(2)用户服务 userService.js

const { generateSnowflakeId } = require('../utils/snowflake');

// 创建用户
function createUser() {
    const userId = generateSnowflakeId(); // 直接调用,共享同一实例
    console.log('生成用户ID:', userId);
    // 输出示例:643133682177019904(18位字符串)
    return { userId, username: 'test-user' };
}
// 测试
createUser();

四、核心特性解析

  1. 单例模式保障唯一性
    无论多少文件引入 snowflake.js,snowflakeInstance 只会初始化一次,避免多实例导致的 ID 冲突。
    首次调用 generateSnowflakeId 时自动初始化,无需手动调用 initSnowflake(灵活配置场景可手动初始化)。
  2. 生产环境适配
    (1)机器 ID 分配
    分布式环境中,每个服务器 / 进程需配置唯一的 mid(机器 ID),避免跨节点 ID 冲突。
    推荐方案:从环境变量读取机器 ID(如 mid: process.env.MACHINE_ID || 1),部署时通过容器 / 配置中心分配。
    (2)大数字精度问题
    雪花 ID 是 18 位数字,超过 JavaScript 的 Number 类型精度上限(2^53),因此 snowflake-id 库默认返回字符串格式,避免精度丢失。
    数据库存储建议:使用 VARCHAR(20) 或 BIGINT 类型(MySQL 的 BIGINT 支持 64 位整数)。
    (3)配置调整
    时间戳位数:默认 40 位支持 34 年,如果需要更长时间范围,可调整 timeBits(如 41 位支持 68 年),但需减少机器位或序列位。
    序列号位数:默认 10 位支持每毫秒 1024 个 ID,高并发场景可调整为 12 位(支持 4096 个 / 毫秒)。
  3. 错误处理
    机器 ID 合法性校验:确保 mid 不超过配置的机器位上限(如 12 位机器位最大支持 4095)。
    实例重复初始化防护:已初始化后再次调用 initSnowflake 会直接返回现有实例,不重复创建。
    五、适用场景
    分布式系统:微服务、多节点部署的应用,需要全局唯一 ID。
    安全性需求:相比自增 ID,雪花 ID 不易暴露业务数据量(如用户 ID=1000 表示仅 1000 个用户)。
    六、注意事项
    时钟回拨问题:雪花算法依赖系统时间,如果服务器时钟回拨,可能生成重复 ID。
    解决方案:生产环境使用 NTP 同步时间,或选择支持时钟回拨处理的雪花算法变种库(如 snowflake-id-plus)。
    机器 ID 上限:默认 12 位机器位支持 4096 个节点,足够大部分中小规模分布式系统;超大规模场景可调整 machineBits。

参考

https://cloud.tencent.com/developer/article/2364487
https://www.npmjs.com/package/snowflake-id
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值