自定义雪花算法生成业务ID
选中新版正文中的对应文字,然后点“定位到这里”。
系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,比如常见的基于数据库自增主键生成的id,随机生成的uuid,亦或者redis自增的计数器等都属于常见的解决方案;本文我们将会重点看一下业界内大名鼎鼎的雪花算法,是如何实现分布式id的
1. 雪花算法
1.1 全局唯一id
雪花算法主要是为了解决全局唯一id,那么什么是全局唯一id呢?它应该满足什么属性呢
基本属性:
- 全局唯一性:不能出现重复的 ID 号,既然是唯一标识,这是最基本的要求;
- 趋势递增:在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能;
- 单调递增:保证下一个 ID 一定大于上一个 ID,例如事务版本号、IM 增量消息、排序等特殊需求;
- 信息安全:如果 ID 是连续的,恶意用户的爬取工作就非常容易做了,直接按照顺序下载指定 URL 即可;如果是订单号就更危险了,竞争对手可以直接知道我们一天的单量。所以在一些应用场景下,会需要 ID 无规则、不规则。
1.2. 雪花算法
雪花算法可以说是业界内生成全局唯一id的经典算法,其基本原理也比较简单

Snowflake 以 64 bit 来存储组成 ID 的4 个部分:
- 最高位占1 bit,值固定为 0,以保证生成的 ID 为正数;
- 中位占 41 bit,值为毫秒级时间戳;
- 中下位占 10 bit,值为机器标识id,值的上限为 1024;
- 末位占 12 bit,值为当前毫秒内生成的不同的自增序列,值的上限为 4096;
从上面的结构设计来看,雪花算法的实现可以说比较清晰了,我们重点看一下它的缺陷
- 时钟回拨问题:由于id的高位依赖于系统的时间戳,因此当服务器时间错乱或者出现时钟回拨,可能导致数据重复
- 集群规模1024台机器,每1ms单机4096个id最大限制
1.3. 实现与使用
目前雪花算法的实现方式较多,通常也不需要我们进行额外开发,如直接Hutool的Snowflake
看下它的核心实现
public class Snowflake implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 默认的起始时间,为Thu, 04 Nov 2010 01:42:54 GMT
*/
public static long DEFAULT_TWEPOCH = 1288834974657L;
/**
* 默认回拨时间,2S
*/
public static long DEFAULT_TIME_OFFSET = 2000L;
private static final long WORKER_ID_BITS = 5L;
// 最大支持机器节点数0~31,一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
private static final long DATA_CENTER_ID_BITS = 5L;
// 最大支持数据中心节点数0~31,一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
// 序列号12位(表示只允许workId的范围为:0-4095)
private static final long SEQUENCE_BITS = 12L;
// 机器节点左移12位
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
// 数据中心节点左移17位
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
// 时间毫秒数左移22位
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
// 序列掩码,用于限定序列最大值不能超过4095
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 4095
/**
* 初始化时间点
*/
private final long twepoch;
private final long workerId;
private final long dataCenterId;
private final boolean useSystemClock;
/**
* 允许的时钟回拨毫秒数
*/
private final long timeOffset;
/**
* 当在低频模式下时,序号始终为0,导致生成ID始终为偶数
* 此属性用于限定一个随机上限,在不同毫秒下生成序号时,给定一个随机数,避免偶数问题。
* 注意次数必须小于{@link #SEQUENCE_MASK},{@code 0}表示不使用随机数。
* 这个上限不包括值本身。
*/
private final long randomSequenceLimit;
/**
* 自增序号,当高频模式下时,同一毫秒内生成N个ID,则这个序号在同一毫秒下,自增以避免ID重复。
*/
private long sequence = 0L;
private long lastTimestamp = -1L;
/**
* 构造,使用自动生成的工作节点ID和数据中心ID
*/
public Snowflake() {
this(IdUtil.getWorkerId(IdUtil.getDataCenterId(MAX_DATA_CENTER_ID), MAX_WORKER_ID));
}
/**
* 构造
*
* @param workerId 终端ID
*/
public Snowflake(long workerId) {
this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
}
/**
* 构造
*
* @param workerId 终端ID
* @param dataCenterId 数据中心ID
*/
public Snowflake(long workerId, long dataCenterId) {
this(workerId, dataCenterId, false);
}
/**
* 构造
*
* @param workerId ...未能定位到原文的划线
已加入星球,可直接知识星球授权登录
二哥编程星球目前包含:
企业级Agent工作流编排项目PaiFlow
Vibe Coding版本的PaiAgent
派聪明RAG AI知识库Java版本+Go版本
微服务 PmHub、技术派、MYDB
求职派JobClaw(OpenClaw/Hermes架构
PaiCLI(类似Claude Code的Agent
派简历(代码已完成)
等实战项目。
企业级Agent工作流编排项目PaiFlow
Vibe Coding版本的PaiAgent
派聪明RAG AI知识库Java版本+Go版本
微服务 PmHub、技术派、MYDB
求职派JobClaw(OpenClaw/Hermes架构
PaiCLI(类似Claude Code的Agent
派简历(代码已完成)
等实战项目。
1. 微信扫右侧的优惠券加入知识星球
2. 解锁星球的实战项目教程和源码: 项目源码+教程获取
真诚点赞 诚不我欺
回复