Snowflake算法是一种在分布式系统中生成全局唯一ID的算法,它保证了生成的ID是趋势递增的,这对于数据库中的索引友好,并且生成效率高。以下是我根据自己需求写的ID生成器工具类。
/**
* 基于Twitter Snowflake算法的分布式唯一ID生成器工具类。
*/
public class SnowflakeIdGeneratorUtils {
// 开始时间戳 (2020-01-01)
/** 开始时间戳,用于计算时间部分的偏移量。 */
private static final long START_TIMESTAMP = 1577836800000L;
// 每一部分占用的位数
/** 序列号占用的位数,用于在同一毫秒内生成多个ID。 */
private static final long SEQUENCE_BITS = 12;
/** 机器标识占用的位数,用于区分不同的机器。 */
private static final long MACHINE_BITS = 5;
/** 数据中心占用的位数,用于区分不同的数据中心。 */
private static final long DATACENTER_BITS = 5;
// 每一部分的最大值
/** 序列号的最大值。 */
private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS);
/** 机器标识的最大值。 */
private static final long MAX_MACHINE = -1L ^ (-1L << MACHINE_BITS);
/** 数据中心标识的最大值。 */
private static final long MAX_DATACENTER = -1L ^ (-1L << DATACENTER_BITS);
// 每一部分向左的位移
/** 机器标识向左的位移量。 */
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
/** 数据中心标识向左的位移量。 */
private static final long DATACENTER_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
/** 时间戳向左的位移量。 */
private static final long TIMESTAMP_SHIFT = DATACENTER_SHIFT + DATACENTER_BITS;
// 成员变量
/** 数据中心ID。 */
private long datacenterId;
/** 机器ID。 */
private long machineId;
/** 序列号,用于在同一毫秒内生成多个ID。 */
private long sequence = 0L;
/** 上一次生成ID的时间戳。 */
private long lastTimestamp = -1L;
/**
* 构造函数,初始化数据中心ID和机器ID。
*
* @param datacenterId 数据中心ID
* @param machineId 机器ID
*/
public SnowflakeIdGeneratorUtils(long datacenterId, long machineId) {
// 校验数据中心ID和机器ID是否在合法范围内
if (datacenterId > MAX_DATACENTER || datacenterId < 0) {
throw new IllegalArgumentException("Datacenter ID can't be greater than " + MAX_DATACENTER + " or less than 0");
}
if (machineId > MAX_MACHINE || machineId < 0) {
throw new IllegalArgumentException("Machine ID can't be greater than " + MAX_MACHINE + " or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 生成下一个ID。
*
* @return 下一个唯一ID
*/
public synchronized long nextId() {
long currentTimestamp = System.currentTimeMillis();
// 如果当前时间小于上一次生成ID的时间,抛出异常(时钟回拨)
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
// 如果当前时间与上一次生成ID的时间相同,则序列号加1
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
// 如果序列号达到最大值,则等待下一毫秒
if (sequence == 0) {
currentTimestamp = getNextMillisecond(lastTimestamp);
}
} else {
// 如果当前时间大于上一次生成ID的时间,重置序列号
sequence = 0L;
}
// 更新上一次生成ID的时间戳
lastTimestamp = currentTimestamp;
// 组合时间戳、数据中心ID、机器ID和序列号生成ID
return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
| (datacenterId << DATACENTER_SHIFT)
| (machineId << MACHINE_SHIFT)
| sequence;
}
/**
* 等待直到下一毫秒。
*
* @param lastTimestamp 上一次生成ID的时间戳
* @return 下一毫秒的时间戳
*/
private long getNextMillisecond(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
// 循环等待直到时间戳大于上一次生成ID的时间戳
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
/**
* 生成订单号。
*
* @return 订单号字符串(最多32位)
*/
public String generateOrderNumber() {
long id = nextId();
String orderNumber = String.valueOf(id);
// 如果订单号长度超过32位,则截取前32位
if (orderNumber.length() > 32) {
orderNumber = orderNumber.substring(0, 32);
}
return orderNumber;
}
}