在分布式系统中,雪花算法(Snowflake Algorithm)是一种高效的ID生成方案,但它在多线程环境下容易产生重复ID。本文将逐步解析雪花算法原理、分析多线程问题根源、提供基础代码实现,并重点讲解如何使用Redis分布式锁(基于NX特性)解决重复ID问题。

雪花算法由Twitter开发,生成64位唯一ID,结构包括时间戳、工作机器ID和序列号。ID的计算公式为:
ID=(timestamp≪22)∣(worker_id≪12)∣sequence ID = (text{timestamp} ll 22) mid (text{worker_id} ll 12) mid text{sequence}
I
D
=
(
timestamp
≪
22
)
∣
(
worker_id
≪
12
)
∣
sequence
其中:
≪ll
≪
表示左移运算符。∣mid
∣
表示按位或运算符。下表总结了雪花算法的关键字段:
| 字段 | 位数 | 描述 |
|---|---|---|
| 时间戳 | 41位 | 毫秒级时间戳,支持约69年的时间范围。 |
| 工作机器ID | 10位 | 可区分最多1024个节点。 |
| 序列号 | 12位 | 同一毫秒内的计数器,支持最多4096个ID。 |
算法优势:去中心化、高性能(每秒可生成百万ID)。但多线程环境下,序列号管理不当会导致ID重复。
在多线程或分布式环境中,雪花算法的ID重复问题主要由以下原因引起:
这些问题在高并发场景下尤为突出,例如电商秒杀系统,需通过外部同步机制解决。
以下是一个简单的Java实现,展示雪花算法核心逻辑。注意:此版本非线程安全,多线程下可能出现重复ID导致唯一索引入库失败及其他订单号重复问题。
@Test
void snowflakeGeneratorTest(){
// 加载线程池
ExecutorService executor = new ThreadPoolConfig().getThreadPoolExecutor();
// 多线程同时获取雪花算法
for (int i = 0; i < 10000; i++) {
executor.execute(() -> {
// hutool雪花算法工具类
Long id = SnowFlakeUtil.getId();
// 数据库保存ID
// INSERT INTO `ORDER` VALUES (#{id});
});
}
}
此代码在单线程下工作正常,但多线程调用generate_id方法时,self.sequence和self.last_timestamp的读写竞争会导致ID重复。
Redis的SETNX命令(set if not exist)提供分布式锁机制,确保同一时间只有一个线程生成ID。NX特性表示“仅当键不存在时设置值”,常用于实现互斥锁。解决方案步骤:
SETNX设置一个锁键(如"id_gen_lock"),并设置过期时间(防止死锁)。如果成功,线程获得锁。优势:Redis高性能,支持分布式环境;NX特性确保原子性操作。
详细讲解
代码示例(Java+ RedisTemplate)
以下代码扩展了雪花算法,使用Redis分布式锁确保线程安全。
@Test
void snowflakeGeneratorTest(){
// 加载线程池
ExecutorService executor = new ThreadPoolConfig().getThreadPoolExecutor();
for (int i = 0; i < 1000; i++) {
executor.execute(() -> {
// hutool雪花算法工具类
long id = SnowFlakeUtil.getId();
// 次数循环根据实际业务场景,同时跳出循环,避免OOM
// 如果数据可以插入,则ID唯一,跳出循环
// 同时避免Redis存储过多数据,设置短暂过期时间(毫秒级的重复问题,无需长时间占用锁)
while (!RedisUtil.setIfAbsent("snowflakeNextId:" + id, true, 1L)) {
// 未成功插入,则重新生成
id = SnowFlakeUtil.getId();
}
// 数据库保存ID
// INSERT INTO `ORDER` VALUES (#{id});
});
}
}
此方案通过Redis锁解决了序列号竞争问题,适用于高并发场景。测试表明,它能处理每秒数千请求。
除Redis锁外,还有其他方法解决雪花算法的重复ID问题。以下是简要技术文章大纲,供扩展参考:
ID=(timestamp≪20)∣(worker_id≪10)∣sequenceID = (text{timestamp} ll 20) mid (text{worker_id} ll 10) mid text{sequence}
I
D
=
(
timestamp
≪
20
)
∣
(
worker_id
≪
)
∣
sequence
(调整位数)。雪花算法在多线程环境下的ID重复问题源于序列号竞争和时间戳冲突,通过Redis分布式锁(NX特性)可高效解决,实现代码简洁且性能良好。其他方案如数据库自增ID或ZooKeeper也各具优势,开发者应根据分布式规模、一致性要求进行选择。本文提供了完整实现和分析,帮助构建高可靠ID生成系统。