Java使用雪花id生成算法详解
作者:码畜c 时间:2023-11-18 21:58:49
什么是雪花算法
雪花算法的本质为生成一个64位长度的具有自增性的分布式全局唯一id。在64bits中,会对不同段的位进行划分。可分为:
符号段
时间戳段
机器码段(data center + worker)
自增序列号段
位段详解
第一位 : 符号位,正数为0。
[2, 42] : 41位时间戳位,表明id的生成时间点(完整时间戳: 起始时间戳 + 41位时间戳)。41位最多能表示的时间为: (2^41-1) / (1000 * 60 * 60 * 24 * 365) 约等为69.73年。
[43, 47] : 5位data center id。data center id + worker id 共10位,最多能表示1024个机器。不同机器保证机器码段的位值不同即可。
[48, 52] : 5位worker id。data center id + worker id 共10位,最多能表示1024个机器。不同机器保证机器码段的位值不同即可。
[53, 64] : 12位自增序列号,用于区分同一毫秒内生成的id。序列号范围: [0, 2^12-1],最多有2^12个,即4096个。
优点
算法简单,基于内存,生成效率高
支持分布式环境下的多节点服务(机器码段),秒内可生成百万个唯一id
基于时间戳 与 同时间戳下自增序列号,生成的id具有自增性
具有业务定制性,根据业务的不同可以对不同段的位数进行变更。比如业务持续时长不会那么久,就可以将时间戳段减少位数,补充给自增序列段,使每一毫秒能生成更多的id。
问题
依赖服务器时间。若服务器时钟回拨,可能会导致生成的id重复。可在代码中新增lastTimeMillis字段,在获取nextId时根据系统当前时间进行判断解决。
但若不进行持久化处理,服务重启后发生时钟回拨依旧会出现重复问题。
实际应用
mybatis plus:使用雪花算法生成id:@TableId(value = “id”, type = IdType.ID_WORKER)。id字段若不指定类型,默认使用雪花算法生成id
Hutool工具包:IdUtil.createSnowflake(workerId, datacenterId);
具体实现
/**
* Created by QQ.Cong on 2022-07-22 / 9:48
*
* @author: CongQingquan
* @Description: Snowflake util
*/
public class SnowflakeUtils {
// ============================== Basic field ==============================//
// Datacenter id
private long datacenterId;
// Worker id
private long workerId;
// Increment sequence
private long sequence;
// ============================== Bits ==============================//
// Bits of datacenter id
private long datacenterIdBits;
// Bits of worker id
private long workerIdBits;
// Bits of sequence
private long sequenceBits;
// ============================== Largest ==============================//
// Largest datacenter id
private long largestDatacenterId;
// Largest worker id
private long largestWorkerId;
// Largest sequence
private long largestSequence;
// ============================== Shift ==============================//
// Left shift num of worker id
private long workerIdShift;
// Left shift num of datacenter id
private long datacenterIdShift;
// Left shift num of timestamp
private long timestampShift;
// ============================== Other ==============================//
// Epoch
private long epoch;
// The timestamp that last get snowflake id
private long lastTimestamp;
// ============================== End ==============================//
public SnowflakeUtils(long dataCenterId, long workerId) {
// Default epoch: 2022-07-22 00:00:00
this(1658419200000L, -1L, dataCenterId, workerId, 5L, 5L, 5L);
}
public SnowflakeUtils(long epoch, long lastTimestamp, long datacenterId, long workerId,
long datacenterIdBits, long workerIdBits, long sequenceBits) {
this.epoch = epoch;
this.lastTimestamp = lastTimestamp;
this.datacenterId = datacenterId;
this.workerId = workerId;
this.sequence = 0L;
this.datacenterIdBits = datacenterIdBits;
this.workerIdBits = workerIdBits;
this.sequenceBits = sequenceBits;
this.largestDatacenterId = ~(-1L << datacenterIdBits);
this.largestWorkerId = ~(-1L << workerIdBits);
this.largestSequence = ~(-1L << sequenceBits);
if (datacenterId > largestDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("The datacenter id param can't be greater than %s or less than 0",
largestDatacenterId));
}
if (workerId > largestWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("The worker id param can't be greater than %s or less than 0",
largestWorkerId));
}
this.workerIdShift = sequenceBits;
this.datacenterIdShift = workerIdShift + workerIdBits;
this.timestampShift = datacenterIdShift + datacenterIdBits;
}
/**
* Get snowflake id
* @return
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 若时钟回退
if (timestamp < lastTimestamp) {
throw new RuntimeException(
"System clock moved backward, cannot to generate snowflake id");
}
// 若当前毫秒内多次生成雪花id
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & largestSequence;
// 序列溢出
if (sequence == 0) {
timestamp = waitUntilNextMilli(timestamp);
}
}
// 若当前毫秒内首次生成雪花id
else {
sequence = 0L;
}
// 更新获取雪花id的时间戳
lastTimestamp = timestamp;
// 生成雪花id (通过位或运算符进行拼接)
return ((timestamp - epoch) << timestampShift) // 时间戳段
| (datacenterId << datacenterIdShift) // 机器码段
| (workerId << workerIdShift) // 机器码段
| sequence; // 自增序列段
}
/**
* Wait until next millisecond
* @param lastTimestamp
* @return
*/
private long waitUntilNextMilli(long lastTimestamp) {
long currentTimeMillis;
do {
currentTimeMillis = System.currentTimeMillis();
}
while (currentTimeMillis <= lastTimestamp);
return currentTimeMillis;
}
/**
* Get util instance
* @param dataCenterId
* @param workerId
* @return
*/
public static SnowflakeUtils getInstance(long dataCenterId, long workerId) {
return new SnowflakeUtils(dataCenterId, workerId);
}
}
来源:https://blog.csdn.net/qq_38074398/article/details/128237868
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Android使用phonegap从相册里面获取照片(代码分享)
Mybatis联合查询的实现方法
![](https://img.aspxhome.com/file/2023/2/68142_0s.png)
基于idea 的 Java中的get/set方法之优雅的写法
![](https://img.aspxhome.com/file/2023/2/60092_0s.jpg)
Android 三种实现定时器详解及实现方法
Java从JDK源码角度对Object进行实例分析
springboot配置文件中使用${}注入值的两种方式小结
![](https://img.aspxhome.com/file/2023/4/100064_0s.png)
Springboot Websocket Stomp 消息订阅推送
![](https://img.aspxhome.com/file/2023/1/106141_0s.jpg)
浅谈C#网络编程详解篇
![](https://img.aspxhome.com/file/2023/7/111287_0s.jpg)
新手学习微服务SpringCloud项目架构搭建方法
SpringBoot项目速度提升之延迟初始化(Lazy Initialization)详解
Java 回调函数深入理解
JAVA对象和字节数组互转操作
Winform 显示Gif图片的实例代码
C#信号量用法简单示例
理解Android中的自定义属性
C#中sleep和wait的区别分析
C#实现根据数字序号输出星期几的简单实例
JAVA抽象类,接口,内部类详解
c# 值类型实例构造器
spring boot如何实现切割分片上传
![](https://img.aspxhome.com/file/2023/7/57487_0s.png)