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
};
三、使用示例,在多文件中使用
-
安装依赖
首先安装 snowflake-id 库:npm install snowflake-id
-
项目目录结构(示例)
project-name/
├── utils/
│ └── snowflake.js # 雪花算法工具类
├── service/
│ ├── userService.js # 用户服务(生成用户ID)
└── app.js # 入口文件 -
多文件中使用(共享同一实例)
(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();
四、核心特性解析
- 单例模式保障唯一性
无论多少文件引入 snowflake.js,snowflakeInstance 只会初始化一次,避免多实例导致的 ID 冲突。
首次调用 generateSnowflakeId 时自动初始化,无需手动调用 initSnowflake(灵活配置场景可手动初始化)。 - 生产环境适配
(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 个 / 毫秒)。 - 错误处理
机器 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