雪花算法(Snowflake Algorithm)是一种用于生成分布式系统中唯一 ID 的算法。它的设计目标是在大规模分布式系统中生成唯一的、有序的、趋势递增的 ID,同时保证高性能和可扩展性。
雪花算法的 ID 结构如下图所示:
0 41 51 63
+------+--------------------------------------+-------+-----+
| sign | timestamp (毫秒级) | 机器ID | 序列号 |
+------+--------------------------------------+-------+-----+
具体来说,这个64位的 ID 包含以下几个部分:
- 符号位(1 bit):始终为0。
- 时间戳(41 bits):记录当前时间与一个固定的起始时间之间的偏移量,精确到毫秒级。41 bits 可以表示约 69 年的时间戳。
- 机器ID(10 bits):由数据中心ID(5 bits)和工作机器ID(5 bits)组成,用来标识不同的机器。最多可以有1024个不同的机器。
- 序列号(12 bits):在同一毫秒内产生的不同 ID 的序列号。可以表示每台机器在同一毫秒内生成的最大序列号数量为4096。
通过将时间戳、机器ID和序列号等组合起来,雪花算法可以在分布式环境下生成唯一的、有序的 64 位 ID。在同一毫秒内,不同的机器生成的 ID 是不会重复的,而同一台机器在同一毫秒内也是可以生成连续递增的 ID。
需要注意的是,雪花算法依赖于系统的时钟准确性。如果系统时钟发生回拨,可能会导致生成重复的 ID 或者无法生成 ID,因此要确保系统时钟的稳定性和准确性。
总的来说,雪花算法是一种简单高效的分布式 ID 生成算法,在分布式系统中广泛应用于唯一标识和排序的需求场景,如数据库主键、日志追踪等。
下面是一个用Java实现雪花算法的示例:
public class SnowflakeIdGenerator {
// 定义起始时间戳
private final static long START_TIMESTAMP = 1625089168000L; // 2021-06-30 00:00:00
// 定义各部分占用的位数
private final static long SEQUENCE_BITS = 12; // 序列号占用的位数
private final static long WORKER_ID_BITS = 10; // 工作机器ID占用的位数
private final static long TIMESTAMP_BITS = 41; // 时间戳占用的位数
// 定义各部分的偏移量
private final static long WORKER_ID_OFFSET = SEQUENCE_BITS; // 工作机器ID的偏移量
private final static long TIMESTAMP_OFFSET = WORKER_ID_OFFSET + WORKER_ID_BITS; // 时间戳的偏移量
// 定义各部分的最大值
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private long workerId; // 工作机器ID
private long sequence = 0L; // 序列号
private long lastTimestamp = -1L; // 上次生成ID的时间戳
public SnowflakeIdGenerator(long workerId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", MAX_WORKER_ID));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate ID.");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0L) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_OFFSET)
| (workerId << WORKER_ID_OFFSET)
| sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
使用示例:
public class Main {
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1);
for (int i = 0; i < 10; i++) {
long id = idGenerator.nextId();
System.out.println(id);
}
}
}
上述示例中,我们创建了一个SnowflakeIdGenerator
类来生成雪花算法ID。在main
方法中,我们创建了一个ID生成器实例,并调用nextId()
方法来获取唯一ID。运行示例代码,会输出10个不重复的ID。
需要注意的是,该示例中用到了系统当前时间戳来作为参考,因此要确保系统时间的准确性,避免时间回拨等问题。另外,如果在多线程环境下使用,需要保证线程安全。