雪花算法生成唯一id
2025/7/10大约 2 分钟
雪花算法生成唯一id
本系统基于改进的Snowflake算法实现分布式唯一ID生成服务,结合Redis实现节点ID的动态分配机制。通过将节点注册中心与时间戳、序列号组合,解决传统Snowflake算法在容器化环境下的节点管理难题,适用于分布式环境下高并发场景的全局唯一ID生成需求。
配置相关信息
spring:
data:
redis:
database: 0
host: localhost
port: 6379
unique-id:
nodeIdBits: 10
sequenceBits: 12
nodeIdKey: snowflake:node_id
创建配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "unique-id")
public class UniqueIdConfig {
private long nodeIdBits; // 节点ID位数
private long sequenceBits; // 序列号位数
private long maxSequence = ~(-1L << sequenceBits); // 序列号最大值
private String nodeIdKey;
}
实现特性:
- 自动绑定yaml配置项
- 动态计算序列号最大值:
maxSequence = 2^sequenceBits - 1
唯一id生成工具类
@Component
public class UniqueIdUtil {
private final StringRedisTemplate redisTemplate;
private final UniqueIdConfig uniqueIdConfig;
private final long nodeId;
private final long startTimestamp; // 起始时间戳(毫秒)
private volatile long lastTimestamp = -1L;
private volatile long sequence = 0L;
public UniqueIdUtil(StringRedisTemplate redisTemplate, UniqueIdConfig uniqueIdConfig) {
this.redisTemplate = redisTemplate;
this.uniqueIdConfig = uniqueIdConfig;
this.startTimestamp = 1600000000000L; // 2020-09-13
this.nodeId = initNodeId();
}
/**
* 初始化节点ID(从Redis获取)
*/
private long initNodeId() {
String key = uniqueIdConfig.getNodeIdKey() + ':' + getApplicationName();
// 使用Redis自增获取唯一节点ID
Long nodeId = redisTemplate.opsForValue().increment(key);
if (nodeId == null || nodeId > getMaxNodeId()) {
throw new RuntimeException("无法获取有效节点ID");
}
// 设置过期时间(防止节点ID耗尽)
redisTemplate.expire(key, 86400, java.util.concurrent.TimeUnit.SECONDS); // 1天
return nodeId;
}
/**
* 获取最大节点ID
*/
private long getMaxNodeId() {
return ~(-1L << uniqueIdConfig.getNodeIdBits());
}
/**
* 获取应用名称(可从配置获取)
*/
private String getApplicationName() {
return "api-gateway:";
}
/**
* 生成唯一ID
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 检查时钟回拨
if (timestamp < lastTimestamp) {
long clockBack = lastTimestamp - timestamp;
if (clockBack > 5) { // 允许5ms的时钟回拨
throw new RuntimeException("时钟回拨异常,回拨时间:" + clockBack + "ms");
}
try {
Thread.sleep(clockBack << 1);
timestamp = System.currentTimeMillis();
} catch (InterruptedException e) {
throw new RuntimeException("时钟回拨处理失败", e);
}
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & uniqueIdConfig.getMaxSequence();
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
// 组装ID:时间戳 << (nodeIdBits + sequenceBits)
// | 节点ID << sequenceBits
// | 序列号
return (timestamp - startTimestamp) << (uniqueIdConfig.getNodeIdBits() + uniqueIdConfig.getSequenceBits())
| (nodeId << uniqueIdConfig.getSequenceBits())
| sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
核心逻辑:
时间差计算:
当前时间 - 固定起始时间(2020-09-13)
二进制位移组合:
┌────────────── 42位时间差 ───────────────┐ ┌10位节点ID┐ ┌12位序列号─┐ 000000000000000000000000000000000000000000 0000000000 000000000000
时钟回拨保护:双倍等待策略
序列号溢出处理:强制等待下一毫秒