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

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

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

一、雪花算法核心原理

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

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

二、snowflake实现

js 复制代码
/**
 * 雪花算法工具类(单例模式)
 * 用于生成全局唯一的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

  2. 项目目录结构(示例)

    project-name/
    ├── utils/
    │ └── snowflake.js # 雪花算法工具类
    ├── service/
    │ ├── userService.js # 用户服务(生成用户ID)
    └── app.js # 入口文件

  3. 多文件中使用(共享同一实例)
    (1)入口文件app.js(提前初始化配置)

js 复制代码
const { initSnowflake } = require('./utils/snowflake');

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

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

(2)用户服务 userService.js

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
相关推荐
白日做梦Q9 小时前
多任务学习:一个模型解决多个视觉问题
学习
非凡ghost9 小时前
3C一体工具箱安卓版(手机维护工具箱)
android·学习·智能手机·软件需求
知识分享小能手9 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04 中的区块链 —— 知识点详解 (23)
学习·ubuntu·区块链
亮子AI9 小时前
【MySQL】node.js 如何判断连接池是否正确连接上了?
数据库·mysql·node.js
刘一说9 小时前
腾讯位置服务JavaScript API GL与JavaScript API (V2)全面对比总结
开发语言·javascript·信息可视化·webgis
a程序小傲9 小时前
【Node】单线程的Node.js为什么可以实现多线程?
java·数据库·后端·面试·node.js
Aotman_10 小时前
JS 按照数组顺序对对象进行排序
开发语言·前端·javascript·vue.js·ui·ecmascript
Mabnus10 小时前
细胞骨架协调蛋白VIM
学习
Hi_kenyon17 小时前
VUE3套用组件库快速开发(以Element Plus为例)二
开发语言·前端·javascript·vue.js
xiaobai17817 小时前
测试工程师入门AI技术 - 前序:跨越焦虑,从优势出发开启学习之旅
人工智能·学习