Java使用雪花id生成算法详解
作者:码畜c 发布时间:2023-11-18 21:58:49
什么是雪花算法
雪花算法的本质为生成一个64位长度的具有自增性的分布式全局唯一id。在64bits中,会对不同段的位进行划分。可分为:
符号段
时间戳段
机器码段(data center + worker)
自增序列号段
位段详解
第一位 : 符号位,正数为0。
[2, 42] : 41位时间戳位,表明id的生成时间点(完整时间戳: 起始时间戳 + 41位时间戳)。41位最多能表示的时间为: (2^41-1) / (1000 * 60 * 60 * 24 * 365) 约等为69.73年。
[43, 47] : 5位data center id。data center id + worker id 共10位,最多能表示1024个机器。不同机器保证机器码段的位值不同即可。
[48, 52] : 5位worker id。data center id + worker id 共10位,最多能表示1024个机器。不同机器保证机器码段的位值不同即可。
[53, 64] : 12位自增序列号,用于区分同一毫秒内生成的id。序列号范围: [0, 2^12-1],最多有2^12个,即4096个。
优点
算法简单,基于内存,生成效率高
支持分布式环境下的多节点服务(机器码段),秒内可生成百万个唯一id
基于时间戳 与 同时间戳下自增序列号,生成的id具有自增性
具有业务定制性,根据业务的不同可以对不同段的位数进行变更。比如业务持续时长不会那么久,就可以将时间戳段减少位数,补充给自增序列段,使每一毫秒能生成更多的id。
问题
依赖服务器时间。若服务器时钟回拨,可能会导致生成的id重复。可在代码中新增lastTimeMillis字段,在获取nextId时根据系统当前时间进行判断解决。
但若不进行持久化处理,服务重启后发生时钟回拨依旧会出现重复问题。
实际应用
mybatis plus:使用雪花算法生成id:@TableId(value = “id”, type = IdType.ID_WORKER)。id字段若不指定类型,默认使用雪花算法生成id
Hutool工具包:IdUtil.createSnowflake(workerId, datacenterId);
具体实现
/**
* Created by QQ.Cong on 2022-07-22 / 9:48
*
* @author: CongQingquan
* @Description: Snowflake util
*/
public class SnowflakeUtils {
// ============================== Basic field ==============================//
// Datacenter id
private long datacenterId;
// Worker id
private long workerId;
// Increment sequence
private long sequence;
// ============================== Bits ==============================//
// Bits of datacenter id
private long datacenterIdBits;
// Bits of worker id
private long workerIdBits;
// Bits of sequence
private long sequenceBits;
// ============================== Largest ==============================//
// Largest datacenter id
private long largestDatacenterId;
// Largest worker id
private long largestWorkerId;
// Largest sequence
private long largestSequence;
// ============================== Shift ==============================//
// Left shift num of worker id
private long workerIdShift;
// Left shift num of datacenter id
private long datacenterIdShift;
// Left shift num of timestamp
private long timestampShift;
// ============================== Other ==============================//
// Epoch
private long epoch;
// The timestamp that last get snowflake id
private long lastTimestamp;
// ============================== End ==============================//
public SnowflakeUtils(long dataCenterId, long workerId) {
// Default epoch: 2022-07-22 00:00:00
this(1658419200000L, -1L, dataCenterId, workerId, 5L, 5L, 5L);
}
public SnowflakeUtils(long epoch, long lastTimestamp, long datacenterId, long workerId,
long datacenterIdBits, long workerIdBits, long sequenceBits) {
this.epoch = epoch;
this.lastTimestamp = lastTimestamp;
this.datacenterId = datacenterId;
this.workerId = workerId;
this.sequence = 0L;
this.datacenterIdBits = datacenterIdBits;
this.workerIdBits = workerIdBits;
this.sequenceBits = sequenceBits;
this.largestDatacenterId = ~(-1L << datacenterIdBits);
this.largestWorkerId = ~(-1L << workerIdBits);
this.largestSequence = ~(-1L << sequenceBits);
if (datacenterId > largestDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("The datacenter id param can't be greater than %s or less than 0",
largestDatacenterId));
}
if (workerId > largestWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("The worker id param can't be greater than %s or less than 0",
largestWorkerId));
}
this.workerIdShift = sequenceBits;
this.datacenterIdShift = workerIdShift + workerIdBits;
this.timestampShift = datacenterIdShift + datacenterIdBits;
}
/**
* Get snowflake id
* @return
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 若时钟回退
if (timestamp < lastTimestamp) {
throw new RuntimeException(
"System clock moved backward, cannot to generate snowflake id");
}
// 若当前毫秒内多次生成雪花id
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & largestSequence;
// 序列溢出
if (sequence == 0) {
timestamp = waitUntilNextMilli(timestamp);
}
}
// 若当前毫秒内首次生成雪花id
else {
sequence = 0L;
}
// 更新获取雪花id的时间戳
lastTimestamp = timestamp;
// 生成雪花id (通过位或运算符进行拼接)
return ((timestamp - epoch) << timestampShift) // 时间戳段
| (datacenterId << datacenterIdShift) // 机器码段
| (workerId << workerIdShift) // 机器码段
| sequence; // 自增序列段
}
/**
* Wait until next millisecond
* @param lastTimestamp
* @return
*/
private long waitUntilNextMilli(long lastTimestamp) {
long currentTimeMillis;
do {
currentTimeMillis = System.currentTimeMillis();
}
while (currentTimeMillis <= lastTimestamp);
return currentTimeMillis;
}
/**
* Get util instance
* @param dataCenterId
* @param workerId
* @return
*/
public static SnowflakeUtils getInstance(long dataCenterId, long workerId) {
return new SnowflakeUtils(dataCenterId, workerId);
}
}
来源:https://blog.csdn.net/qq_38074398/article/details/128237868
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- springboot整合redis主从sentinel一主二从三sentinel配置1、master:127.0.0.1:63792、sla
- springboot配置mysql数据库spring.datasource.url报错spring.datasource.url=jdbc:
- 实例如下:import java.lang.reflect.Field;import java.lang.reflect.Invocatio
- eclipse中的javac命令与java命令一、eclipse的javac命令:当eclipse对.java(源文件)文件进行保存操作时(
- * 其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承P
- Maven使用说明及规范此文档主要说明Maven的基础使用方式,以及在使用过程过程中需要遵守哪些默认的准则。我们工作中会经常写maven的配
- 我们知道在编程时许多操作(如更新UI)需要在主线程中完成,而且,耗时操作(如网络连接)需要放在子线程中,否则会引起ANR。所以我们常使用Ha
- 执行引擎也只有几个概念, JVM方法调用和执行的基础数据结构是 栈帧, 是内存区域中 虚拟机栈中的栈元素, 每一个方法的执行就对应着一个栈帧
- sqlite是啥?1、一种轻型数据库2、关系型数据库3、占用资源很低,几百K内存,适合嵌入式设备4、支持windows、linux、unix
- Quartz与Spring集成方式:1.MethodInvokeJobDetailFactoryBean2.JobDetailBean下面分
- 内容简介本篇将介绍 Flutter 中如何完成图片上传,以及上传成功后的表单提交。涉及的知识点如下:图片选择插件wechat_assets_
- 堆区:只存放类对象,线程共享;方法区:又叫静态存储区,存放class文件和静态数据,线程共享;栈区:存放方法局部变量,基本类型变量区、执行环
- 这个小游戏是我和我姐们儿的JAVA课程设计,也是我做的第一个JAVA项目,适合初学者,希望能帮到那些被JAVA课设所困扰的孩纸们~~~一、该
- 首先来看看以下程序将会打印出什么:class Dog { public static void bark
- 前 言🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端☕专栏简介:深入、全面、系统的介绍消息中间件🌰 文章简介
- 多数据源创建数据库CREATE DATABASE mybatis_plus_1;USE mybatis_plus_1;CREATE TABL
- 延迟加载1 使用延迟加载意义在进行数据查询时,为了提高数据库查询性能,尽量使用单表查询,因为单表查询比多表关联查询速度要快。如果查询单表就可
- 问题描述通过FeignClient调用微服务提供的分页对象IPage报错{"message": "Type d
- 前言:数据抽象是一种仅向用户显示基本细节的属性。不向用户显示琐碎或非必需的单元。例如:汽车被视为汽车而不是其单个组件。数据抽象也可以定义为仅
- 在未分享整个查询分页的执行代码之前,先了解一下执行流程。1.总体上是利用mybatis的插件 * ,在sql执行之前拦截,为查询语句加上li