Java分布式ID生成方案:从原理到实战详解

一、引言
在分布式系统中,ID生成是一个常见且关键的问题。分布式ID不仅需要保证唯一性,还要满足高性能、高可用性等要求。本文将深入探讨Java分布式ID生成方案,从原理到实战,为大家带来一套完整的解决方案。
二、分布式ID生成方案概述
分布式ID生成方案主要分为以下几种:
1. 数据库自增ID
2. UUID
3. 雪花算法(Snowflake)
4. 百度开源的Leaf算法
5. Twitter开源的Snowflake算法
本文将重点介绍雪花算法和Leaf算法。
三、雪花算法原理及实现
雪花算法是由Twitter开源的,它能够生成一个64位的唯一ID。这个ID由以下部分组成:
1. 时间戳(41位):精确到毫秒的时间戳,可以表示41位。
2. 数据中心ID(5位):表示数据中心ID,可以表示5位。
3. 机器ID(5位):表示机器ID,可以表示5位。
4. 序列号(12位):表示同一毫秒内生成的ID序列,可以表示12位。
雪花算法的原理如下:
1. 首先获取当前时间戳,并与上一次生成ID的时间戳进行比较。
2. 如果时间戳没有变化,则递增序列号。
3. 如果时间戳发生变化,则重置序列号,并更新上一次生成ID的时间戳。
4. 按照雪花算法的格式生成ID。
以下是雪花算法的Java实现:
```java
public class SnowflakeIdWorker {
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long twepoch = 1288834974657L;
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long lastTimestamp = -1L;
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
```
四、Leaf算法原理及实现
Leaf算法是由百度开源的,它是一种基于Twitter Snowflake算法的改进方案。Leaf算法的主要特点是将数据中心ID和机器ID合并为一个ID,从而减少ID长度。
Leaf算法的原理如下:
1. 首先获取当前时间戳,并与上一次生成ID的时间戳进行比较。
2. 如果时间戳没有变化,则递增序列号。
3. 如果时间戳发生变化,则重置序列号,并更新上一次生成ID的时间戳。
4. 按照Leaf算法的格式生成ID。
以下是Leaf算法的Java实现:
```java
public class LeafIdWorker {
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long twepoch = 1288834974657L;
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long lastTimestamp = -1L;
public LeafIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
```
五、总结
本文深入探讨了Java分布式ID生成方案,包括雪花算法和Leaf算法。通过了解这些算法的原理和实现,我们可以更好地应对分布式系统中ID生成的问题。在实际应用中,我们可以根据业务需求选择合适的分布式ID生成方案,以提高系统的性能和可用性。






