snowflake(雪花算法) 生成分布式 ID
snowflake(雪花算法) 生成分布式 ID
1、常见的分布式 ID 实现
在如今的环境下,对于分布式 ID 的实现有以下几种方式:
- UUID
- Redis
- snowflake
- 美团 leaf - 雪花算法的变形
- 百度 UidGenerator - x雪花算法的变形
- 滴滴 Tinyid
这里对于 snowflake 做基本的介绍及实现。
2、snowflake 介绍
snowflake,Twitter 开源的一种分布式 ID 生成算法。基于64位数实现,下图为 snowflake 算法的ID构成图:
- 第1位置为0。
- 第2-42位是相对时间戳,通过当前时间戳减去一个固定的历史时间戳生成。
- 第43-52位是机器号workerID,每个Server的机器ID不同。
- 第53-64位是自增ID。
3、优缺点
3.1、优点
- 不依赖数据库等第三方系统,生成 ID 的性能非常高。
- 时间戳在高位,自增ID在低位,整个ID呈现趋势递增。
- 根据不同业务分配 bit 位数,相当灵活。
3.2、缺点
-
强烈依赖机器时钟,如果发生时钟回拨(即服务器当前取的时间小于上次取的时间),会导致发号重复。
时钟回拨:首先假定当前的北京时间是 9:00:00。另外上次生成 ID 的时候,服务器获取的时间 lastTimestamp=10:00:00,而现在服务器获取的当前时间 timestamp=09:00:00,这就相当于服务器之前是获取了一个未来时间,现在突然
跳跃
到当前时间,这种场景我们称之为时钟回拨
或时钟跳跃
。出现原因:服务器时钟可能会因为各种原因发生不准,而网络中会提供 NTP 服务来做时间校准,因此在做校准的时候,服务器时钟就会发生时钟的跳跃或者回拨问题。
4、代码实现
以下是 snowflake 的代码实现,由于第一位是标志位没有用到,因此生成 63 位的 分布式 ID:
/**
* 生成分布式ID(雪花算法)
*/
public class SnowflakeUtilV2 {
//开始时间
private final static long START_TIME = 16538551000L;
//序列号位数
private final static int SEQUENCE_BIT = 12;
//服务器位数
private final static int SERVER_BIT = 5;
//机器位数
private final static int MACHINE_BIT = 5;
//时间左移位数
private final static int TIME_LEFT_BIT = SEQUENCE_BIT + SERVER_BIT + MACHINE_BIT;
//服务器左移位数
private final static int SEVER_LEFT_BIT = SEQUENCE_BIT + SERVER_BIT;
//时间左移位数
private final static int MACHINE_LEFT_BIT = SEQUENCE_BIT;
//序列号最大值
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
//服务器id默认值
private final static long serverId = 1;
//机器id默认值
private final static long machineId = 1;
//序列号默认值
private long sequence = 0L;
//上次时间戳默认值
private long lastTime = -1L;
/**
* 返回63位的分布式id(不返回标识位)
* 时间戳(41位) + 服务器id(5位) + 机器id(5位) + 序列号(12位)
*
* @return
*/
public synchronized long generateId() {
long currentTime = getCurrentTime();
//当前时间小于上次时间,抛出异常
if (currentTime < lastTime) {
throw new RuntimeException("current time is error");
}
//同一个时间戳,序列号加1
if (currentTime == lastTime) {
sequence = sequence + 1;
//当前时间的序列号已满,取下个时间的序列号
if (sequence >= MAX_SEQUENCE) {
currentTime = getNextTime();
}
} else {
//当前时间大于上次时间,序列号置为0
sequence = 0L;
}
//修改上次时间
lastTime = currentTime;
return (currentTime - START_TIME) << TIME_LEFT_BIT
| serverId << SEVER_LEFT_BIT
| machineId << MACHINE_LEFT_BIT
| sequence;
}
/**
* 获取当前时间戳
*
* @return
*/
private static long getCurrentTime() {
return System.currentTimeMillis();
}
/**
* 获取下个时间戳
*
* @return
*/
private long getNextTime() {
long currentTime = getCurrentTime();
while (currentTime == lastTime) {
currentTime = getCurrentTime();
}
return currentTime;
}
}
参考资料:
自我控制是最强者的本能-萧伯纳