SnowFlake 介绍
- SnowFlake 中文意思为雪花,故称为雪花算法。
- 最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。
- 2014年开源 scala 语言版本。
组成部分
- 首位:1bit,固定为0。
- 时间戳:41bit,((2^41 ) - 1) / (1000x60x60x24x365) 大约可以使用
69
年。 - 机器码: 10bit,一般前5bit用户机房,后5bit用于服务器,共可部署2^5 x 2^5 =
1024
台服务器。 - 序列号:12bit,同一毫秒时间戳,通过序列号来递增区分,1ms可以容纳 (2^12) -1=
4095
个id,超过则获取下一毫秒。
java算法实现雪花算法
package com.example.demo.utils;
/**
* @author 谢阳
* @version 1.8.0_131
* @date 2022/08/12 20:30:00
* @description 雪花算法
*/
public class SnowFlake {
/**
* 组成部分 最高符号位 + 时间戳 + (机房id+机房id) + 序列号
*/
// 修复时间戳 2022-08-12 20:30:00
private static final long FIX_TIME_STAMP = 1660307400L;
// 机房id
private final long computerRoomId;
// 机器id
private final long machineId;
// 序列号
private long sequence = 0L;
/**
* 所占用的bit个数
*/
// 时间戳41bit
// 5bit机房id
private static final long COMPUTER_ROOM_BIT_CNT = 5L;
// 5bit机器id
private static final long MACHINE_BIT_CNT = 5L;
// 12bit序列号
private static final long SEQUENCE_BIT_CNT = 12L;
/**
* 位移的位数
*/
// 机器id 左移12位
private static final long MACHINE_ID_SHIFT = SEQUENCE_BIT_CNT;
// 机房id 左移17位
private static final long COMPUTER_ROOM_ID_SHIFT = MACHINE_ID_SHIFT + MACHINE_BIT_CNT;
// 时间戳 左移22位
private final static long TIME_STAMP_SHIFT = COMPUTER_ROOM_ID_SHIFT + COMPUTER_ROOM_BIT_CNT;
/**
* 聚合信息
*/
// 支持最大的机房id机房id 5bit
private static final long MAX_COMPUTER_ROOM_ID = ~(-1 << COMPUTER_ROOM_BIT_CNT);
// 支持最大的机器id 5bit
private static final long MAX_MACHINE_ID = ~(-1 << MACHINE_BIT_CNT);
// 序列号支持的最大的个数 12bit
private static final long SEQUENCE_MASK = ~(-1 << SEQUENCE_BIT_CNT);
// 上一次生成的时间戳
private long lastTimeStamp = -1L;
/**
* @param computerRoomId 机房id
* @param machineId 机器id
*/
public SnowFlake(long computerRoomId, long machineId) {
if (computerRoomId < 0 || computerRoomId > MAX_COMPUTER_ROOM_ID) {
throw new IllegalArgumentException("computerRoomId 不在范围");
}
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
throw new IllegalArgumentException("computerRoomId 不在范围");
}
this.computerRoomId = computerRoomId;
this.machineId = machineId;
}
/**
* @return 返回毫秒级时间戳
*/
private long getCurrentTime() {
return System.currentTimeMillis();
}
/**
* @return 雪花刷法生成 id
*/
public synchronized long getNextId() {
// 拿到时间戳
long currentTimeStamp = getCurrentTime();
// 时间戳回拨问题
if (currentTimeStamp < lastTimeStamp) {
throw new RuntimeException(
String.format("可能出现服务器时钟回拨问题,请检查服务器时间。当前服务器时间戳:%d,上一次使用时间戳:%d", currentTimeStamp,
lastTimeStamp));
}
// 时间为同一毫秒时间,sequence + 1
if (currentTimeStamp == lastTimeStamp) {
// 序列号 + 1
sequence = (sequence + 1) & SEQUENCE_MASK;
// 序列号用完
if (sequence == 0) {
// 获取下一个毫秒级
currentTimeStamp = getNextMillis();
}
} else {
sequence = 0;
}
// 记录上一次时间戳
lastTimeStamp = currentTimeStamp;
// 生成唯一id
return ((currentTimeStamp - FIX_TIME_STAMP) << TIME_STAMP_SHIFT) |
(computerRoomId << COMPUTER_ROOM_ID_SHIFT) |
(machineId << MACHINE_ID_SHIFT) |
sequence;
}
/**
* @return 下一毫秒
*/
private long getNextMillis() {
long currentTimeStamp = getCurrentTime();
while (currentTimeStamp <= lastTimeStamp) {
currentTimeStamp = getCurrentTime();
}
return currentTimeStamp;
}
}
springBoot整合使用雪花算法
结构如下:
第一步:配置yaml
SnowFlake:
computerRoomId: 0
machineId: 0
第二步:编写SnowFlake雪花算法
略.
第三步:编写SnowFlakeConfig配置文件
package com.example.demo.config;
import com.example.demo.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 谢阳
* @version 1.8.0_131
* @date 2022/8/12 21:25
* @description
*/
@Slf4j
@Configuration
public class SnowFlakeConfig {
@Value("${SnowFlake.computerRoomId}")
private long computerRoomId;
@Value("${SnowFlake.machineId}")
private long machineId;
@Bean
public SnowFlake snowFlake() {
return new SnowFlake(computerRoomId,machineId);
}
}
测试代码如下:
package com.example.demo;
import com.example.demo.utils.SnowFlake;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Autowired
SnowFlake snowFlake;
@Test
void contextLoads() {
for (int i = 0; i < 5; i++) {
long flakeId = snowFlake.getNextId();
System.out.println(i + " = " +flakeId);
}
}
}