雪花算法(SnowFlake) ,最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID,最终生成一个唯一long类型,id基本是递增,对于生成索引,存储空间和数据库索引性能有很大优势。
long类型数值总共有64位,二进制表示 正整数(1位)-毫秒时间戳(41位)-6位机器码-16位自增id
1位 总是0表示正整数
41位 毫秒时间戳
6位 机器码,集群中最多的2^6=64台机器
16位 剩余自增序列 2^16=65536位,每毫秒每个实例最多产生这么多位唯一id
备注:一般的算法是10位机器码,机器编码5位+业务编码5位(或者机房编号)
以下是简单生成id代码实现
public class IdGenerator {
/** 最后的执行时间 **/
private long last_ms;
/** 机器码 **/
private long workId;
/** 同一毫秒 **/
private int sequence;
/** 初始化时间戳 **/
private static long init_epoch = DateUtil.parseDate("2023-05-13 00:00:00").getTime();
public IdGenerator(long workerId) {
if (workerId > 63) {
throw new IllegalArgumentException("机器码最大64位");
}
this.workId = workerId;
//初始化赋值最后处理时间
last_ms = System.currentTimeMillis();
}
/**
* 生成id的入口
*/
public synchronized long generateId() {
long current = System.currentTimeMillis();
//时钟回拨,线程休眠,
if (current < last_ms) {
//如果回拨时间很长抛出异常
if (last_ms - current > 30 * 1000) {
throw new RuntimeException("时钟回拨超过30s");
}
//休眠
ThreadUtil.safeSleep(last_ms - current);
return generateId();
}
if (current > last_ms) {
last_ms = current;
sequence = 0;
return createId(current);
}
//如果序列号超过最大值,休眠1ms,再获取id值
if ( ++sequence > 65535) {
ThreadUtil.safeSleep(1);
sequence = 0;
return generateId();
}
return createId(current);
}
/**
* 位运算拼接id
*/
private long createId(long current) {
return ((current - init_epoch) << 22)
|(workId << 16)
|sequence;
}
public static void main(String[] args) {
IdGenerator idGenerator = new IdGenerator(0);
//结果
Set<Long> idSet = new ConcurrentHashSet<>();
long s = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
long id = idGenerator.generateId();
if (idSet.contains(id)) {
System.out.println("存在重复id");
} else {
idSet.add(id);
}
}
System.out.println("执行时间" + (System.currentTimeMillis() - s));
}
}
分布式id性能还是非常优秀的,单线程10万id生成100ms内