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
相关推荐
起这个名字2 小时前
Webpack——插件实现的理解
前端·javascript·node.js
二川bro3 小时前
第51节:Three.js源码解析 - 核心架构设计
开发语言·javascript·ecmascript
T***u3333 小时前
后端缓存技术学习,Redis实战案例
redis·学习·缓存
Gorgous—l4 小时前
数据结构算法学习:LeetCode热题100-图论篇(岛屿数量、腐烂的橘子、课程表、实现 Trie (前缀树))
数据结构·学习·算法
im_AMBER4 小时前
算法笔记 13 BFS | 图
笔记·学习·算法·广度优先
djk88884 小时前
多标签页导航后台模板 html+css+js 纯手写 无第三方UI框架 复制粘贴即用
javascript·css·html
烤麻辣烫4 小时前
黑马程序员苍穹外卖(新手) DAY3
java·开发语言·spring boot·学习·intellij-idea
Hilaku4 小时前
别再吹性能优化了:你的应用卡顿,纯粹是因为产品设计烂🤷‍♂️
前端·javascript·代码规范
驯狼小羊羔4 小时前
学习随笔-hooks和mixins
前端·javascript·vue.js·学习·hooks·mixins