雪花算法-生成全局唯一ID

雪花算法-⽣成全局唯⼀ID
传统的⽣成ID的⽅法有以下⼏个:
Java的UUID
mysql的⾃增主键
利⽤Redis的INCR
以上⽅法都存在⼀定的问题。
UUID⽣成的是以下⼀串36位的16进制数
6d62ff8c-66d3-43e1-8009-44fec60d3b30
但是⽤UUID当主键的话存在⼀定问题,⾸先,它是⽆序的,因为mysql主键都会⾃动⽣成唯⼀索引,如果主键⽆序的话,插⼊⼀条数据维护索引的代价会很⾼,造成插⼊数据的性能降低。其次,UUID的长度过长,mysql官⽅推荐主键的长度越短越好,显然,36位的UUID不满⾜条件。
如果使⽤mysql的⾃增主键,分布式环境下如果并发量过⾼,会导致mysql服务器压⼒过⼤,为了防⽌My
SQL崩溃,这时候就需要搭建MySQL集,这时候就需要去设置步长,这样做的成本过⾼。
因为Redis是单线程的,天⽣满⾜原⼦性,可以⽤Redis的INCR去⽣成唯⼀ID来获取更⾼的吞吐量。但是这样也存在问题,和mysql⼀样,在集环境下,需要设置不同的增长补偿,⽽且redis还涉及到key的过期时间,这样去维护⼀个Redis集成本是很⾼的。
⽽现在分布式系统最常⽤的⽣成全局唯⼀ID的⽅法是雪花算法snowFlake。
雪花算法⽣成的结果是⼀位64bit的整数,为⼀个long类型(转换成字符串最多为19位)
雪花算法结构图如下
从左到右,第⼀位为符号位,0表⽰正,1表⽰符,不⽤。
时间戳(毫秒转化为年):2^41/(3652460601000)=69.73年。说明雪花算法可表⽰的范围为69年(从1970年开始),说明雪花算法能⽤到2039年。
12bit-序列号:表⽰每个机房的每个机器每毫秒可以产⽣2^12-1(4095)个不同的ID序号,雪花算法⽣成的ID整个分布式系统不会重复(因为有workerId和datacenterId)
由上可见⽤雪花算法⽣成的ID既是全局唯⼀的,⼜是递增的,⼜不需要任何维护成本,所以雪花算法相⽐之下是最好⽣成全局唯⼀ID的⽅法。
public class SnowFlakeGenerateIdWorker {
/**
* 开始时间截
*/
private final long twepoch = 1420041600000L;
/
**
* 机器id所占的位数
*/
private final long workerIdBits = 5L;
/**
* 数据标识id所占的位数
*/
private final long datacenterIdBits = 5L;
/**
* ⽀持的最⼤机器id,结果是31 (这个移位算法可以很快的计算出⼏位⼆进制数所能表⽰的最⼤⼗进制数)    */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
居家新主张
/**
* ⽀持的最⼤数据标识id,结果是31
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/**
* 序列在id中占的位数
*/
private final long sequenceBits = 12L;
/**
* 机器ID向左移12位
*/
private final long workerIdShift = sequenceBits;
/**
* 数据标识id向左移17位(12+5)
*/
ertlprivate final long datacenterIdShift = sequenceBits + workerIdBits;
/**
* 时间截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/**
* ⽣成序列的掩码,这⾥为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/**
永暑礁* ⼯作机器ID(0~31)
*/
private long workerId;
/**
* 数据中⼼ID(0~31)
*/
private long datacenterId;
/**
* 毫秒内序列(0~4095)
*/
private long sequence = 0L;
/**
* 上次⽣成ID的时间截
*/
private long lastTimestamp = -1L;
/**
* 构造函数
*
* @param workerId    ⼯作ID (0~31)
* @param datacenterId 数据中⼼ID (0~31)
*/
public SnowFlakeGenerateIdWorker(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;
}
/**
* 获得下⼀个ID (该⽅法是线程安全的)
*
* @return long
*/
public synchronized long nextId() {
long timestamp = timeGen();
timestamp = generateId(timestamp);
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
private long generateId(long timestamp){
//如果当前时间⼩于上⼀次ID⽣成的时间戳,说明系统时钟回退过这个时候应当抛出异常
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;
}
/
/上次⽣成ID的时间截
lastTimestamp = timestamp;
return timestamp;
}
/**
*获得下⼀个ID (string)
**/
public synchronized String generateNextId() {
long timestamp = timeGen();
timestamp = generateId(timestamp);
//移位并通过或运算拼到⼀起组成64位的ID
return String.valueOf(((timestamp - twepoch) << timestampLeftShift)
反刍动物营养学
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence);
}
/**
* 阻塞到下⼀个毫秒,直到获得新的时间戳
*
* @param lastTimestamp 上次⽣成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowFlakeGenerateIdWorker snowFlakeGenerateIdWorker =
new SnowFlakeGenerateIdWorker(0L,0L);
String id = ateNextId();
System.out.println(id);
}乌鞘岭隧道
}
springboot整合snakeFlake。这⾥推荐⼤家⼀个很好⽤的⼯具包hutool
Hutool是⼀个Java⼯具包类库,对⽂件、流、加密解密、转码、正则、线程、XML等JDK⽅法进⾏封装,组成各种Util⼯具类
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.5</version>
</dependency>

本文发布于:2024-09-22 09:30:00,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/244906.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:算法   雪花   时间   需要
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议