有没有深入研究过雪花算法的老哥啊主流的雪花算法实现的时间为为啥都喜欢用当前时间戳减去一个起始时间呢直接用时间戳不好么这个感觉不准啊多台机器相同的时间戳减去相同的起始时间不也会重复么 java实现下
雪花算法是一种分布式唯一ID生成算法,主要用于解决分布式系统中的唯一ID生成问题。雪花算法的实现通常都会使用当前时间戳减去一个起始时间来生成ID,这是因为当前时间戳是唯一的,并且可以保证递增,但是如果直接使用时间戳作为ID,可能会存在以下问题:
-
时间戳精度问题:时间戳通常只有毫秒级别的精度,如果并发量较大,可能会出现重复的ID。
-
时钟回拨问题:由于系统时钟可能会出现回拨的情况,如果直接使用时间戳作为ID,可能会出现不连续的情况。
因此,为了解决这些问题,雪花算法中通常会使用当前时间戳减去一个起始时间来生成ID,起始时间通常是一个固定的值,可以作为系统的启动时间或者是一个特定的时间点。这样可以保证ID的唯一性和递增性,同时避免了时间戳精度和时钟回拨的问题。
以下是一个简单的Java实现示例:
public class SnowflakeIdGenerator {
// 起始时间戳
private static final long START_TIMESTAMP = 1629302400000L; // 2021-08-19 00:00:00
// 机器ID所占的位数
private static final long WORKER_ID_BITS = 5L;
// 数据中心ID所占的位数
private static final long DATA_CENTER_ID_BITS = 5L;
// 序列号所占的位数
private static final long SEQUENCE_BITS = 12L;
// 支持的最大机器ID,结果是31
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
// 支持的最大数据中心ID,结果是31
private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
// 机器ID向左移12位
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
// 数据中心ID向左移17位
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
// 时间戳向左移22位
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
// 序列号的最大值,结果是4095
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// 数据中心ID
private long dataCenterId;
// 机器ID
private long workerId;
// 序列号
private long sequence = 0L;
// 上次生成ID的时间戳
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long dataCenterId, long workerId) {
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException("Data center ID can't be greater than " + MAX_DATA_CENTER_ID + " or less than 0");
}
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException("Worker ID can't be greater than " + MAX_WORKER_ID + " or less than 0");
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}
public synchronized long generateId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) |
(dataCenterId << DATA_CENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
在该实现中,START_TIMESTAMP是起始时间戳,WORKER_ID_BITS、DATA_CENTER_ID_BITS和SEQUENCE_BITS是机器ID、数据中心ID和序列号所占的位数,MAX_WORKER_ID和MAX_DATA_CENTER_ID是支持的最大机器ID和数据中心ID,WORKER_ID_SHIFT、DATA_CENTER_ID_SHIFT和TIMESTAMP_SHIFT是机器ID、数据中心ID和时间戳的位移量,MAX_SEQUENCE是序列号的最大值。在生成ID时,会先获取当前时间戳,然后根据时间戳、数据中心ID、机器ID和序列号生成ID,如果当前时间戳小于上次生成ID的时间戳,则抛出异常,如果当前时间戳相等,则递增序列号,如果序列号超过最大值,则等待下一毫秒再生成ID。
原文地址: https://www.cveoy.top/t/topic/bgCY 著作权归作者所有。请勿转载和采集!