Java中雪花算法的实现

背景

在id生成器中,我们自己手写一个自增的id生成器很简单,也很好用。但这只是单机中的id生成器,当我们在集群中使用时,一个集群就会有一个id生成器实例,就意味着每一个集群的id都会从0开始,最后就会导致id出现重复的情况。这种情况下有没有更好的id生成器呢?

雪花算法(Snowflake)最初是在Twitter的内部产生的,其主要背景是Twitter在高并发环境下对唯一ID生成的需求。由于Twitter面临着大量的用户和数据交互,因此需要一种高效且能够生成全局唯一ID的算法来支持其分布式系统的运行。雪花算法就是在这样的背景下应运而生,并且由于其高效、可靠的特点,逐渐被广泛使用。(世界上没有一片雪花是相同的)

雪花算法

用一个接地气的说法来解释一下雪花算法。(此解释不一定是准确算法,但大致意思正确,为了更好地理解)

我们可以对每个机房进行id编号,在这基础上再对每个机房中的服务进行编号,这样生成的每一个id就不会出现重复的情况,就实现了全局id的唯一。

雪花算法的实现

机房号

机器号

时间戳

序列号(同一个机房同一个机器同一个时间时,想要并发产生大量id就使用到了序列号,就不会出现重复的id)。

我们可以设置机房号位5bit,机器号为5bit,时间戳是64bit以内,根据序列号使用12bit来规定,我们可以将时间戳设置为 42bit。

雪花算法的实现在宏观上来看就是将42位的时间戳左移22位让他占在id的前42位,然后让机房号占用第43到47位,以此类推,最后生成一个唯一的id。

我们先将这些号码位数规定好

//起始时间戳public static final long START_STAMP = DateUtils.get("2024-1-1").getTime();public static final long ROOM_BIT = 5L;public static final long MACHINE_BIT = 5L;public static final long SEQUENCE_BIT = 12L;

然后将他们的最大值设定好来防止他们超过最大值而产生重复值。

public static final long ROOM_MAX = ~(-1L << ROOM_BIT);
public static final long MACHINE_MAX = ~(-1L << MACHINE_BIT);
public static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BIT);

然后设定好他们要左移的位数。

public static final long TIME_STAMP_LEFT = ROOM_BIT + MACHINE_BIT + SEQUENCE_BIT;public static final long ROOM_LEFT = MACHINE_BIT + SEQUENCE_BIT;public static final long MACHINE_LEFT = SEQUENCE_BIT;

声明好各个id。

private long roomId;private long machineId;private AtomicLong sequenceId = new AtomicLong(0);

我们现在需要一个构造器来指定机房号和机器号。

public IdGenerator(long roomId, long machineId) {if (roomId > ROOM_MAX || machineId > MACHINE_MAX){throw new IllegalArgumentException("机房号id或者机器号id不合法");}this.roomId = roomId;this.machineId = machineId;}

然后是获取id的逻辑,大体思路是获取当前与开始时间戳的差值时间戳,比较一下是否比上一次时间戳小,如果小的话,就抛出时钟回拨异常,然后与上次记录的时间戳进行比较,如果相同就使用原子类不断累加序列号,最后将这些id左移后并进行返回。

public long getId(){long currentTime = System.currentTimeMillis();long timeStamp = currentTime - START_STAMP;if (timeStamp < lastTimeStamp){throw new RuntimeException("出现时钟回拨问题。");}if (timeStamp == lastTimeStamp){sequenceId.incrementAndGet();if (sequenceId.longValue() >= SEQUENCE_MAX){timeStamp = getNextTimeStamp();sequenceId.set(0);}} else {sequenceId.set(0);}lastTimeStamp = timeStamp;long sequence = sequenceId.longValue();return timeStamp << TIME_STAMP_LEFT | roomId << ROOM_LEFT | machineId << MACHINE_LEFT| sequence;}

在序列号累加之中可能会出现累加后大于最大值的情况,这时候我们可以再次获取一次时间戳,开始下一次的时间戳来生成id。

 private long getNextTimeStamp() {long timeStamp = System.currentTimeMillis() - START_STAMP;while (timeStamp == lastTimeStamp){timeStamp = System.currentTimeMillis() - START_STAMP;}return timeStamp;}

以上就是雪花算法的实现,下面附上全部代码。

package com.mhz;import java.util.concurrent.atomic.AtomicLong;/*** 请求id生成器*/
public class IdGenerator {//起始时间戳public static final long START_STAMP = DateUtils.get("2024-1-1").getTime();public static final long ROOM_BIT = 5L;public static final long MACHINE_BIT = 5L;public static final long SEQUENCE_BIT = 12L;public static final long ROOM_MAX = ~(-1L << ROOM_BIT);public static final long MACHINE_MAX = ~(-1L << MACHINE_BIT);public static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BIT);public static final long TIME_STAMP_LEFT = ROOM_BIT + MACHINE_BIT + SEQUENCE_BIT;public static final long ROOM_LEFT = MACHINE_BIT + SEQUENCE_BIT;public static final long MACHINE_LEFT = SEQUENCE_BIT;private long roomId;private long machineId;private AtomicLong sequenceId = new AtomicLong(0);private long lastTimeStamp;public IdGenerator(long roomId, long machineId) {if (roomId > ROOM_MAX || machineId > MACHINE_MAX){throw new IllegalArgumentException("机房号id或者机器号id不合法");}this.roomId = roomId;this.machineId = machineId;}public long getId(){long currentTime = System.currentTimeMillis();long timeStamp = currentTime - START_STAMP;if (timeStamp < lastTimeStamp){throw new RuntimeException("出现时钟回拨问题。");}if (timeStamp == lastTimeStamp){sequenceId.incrementAndGet();if (sequenceId.longValue() >= SEQUENCE_MAX){timeStamp = getNextTimeStamp();sequenceId.set(0);}} else {sequenceId.set(0);}lastTimeStamp = timeStamp;long sequence = sequenceId.longValue();return timeStamp << TIME_STAMP_LEFT | roomId << ROOM_LEFT | machineId << MACHINE_LEFT| sequence;}private long getNextTimeStamp() {long timeStamp = System.currentTimeMillis() - START_STAMP;while (timeStamp == lastTimeStamp){timeStamp = System.currentTimeMillis() - START_STAMP;}return timeStamp;}public static void main(String[] args) {IdGenerator idGenerator = new IdGenerator(1,2);for (int i = 0; i < 1000; i++) {new Thread(() -> System.out.println(idGenerator.getId())).start();}}
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/806744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IO流【 文件字符输入、出流;带缓冲区的字符输入、出流;对象流】

day36 IO流 字符流继承图 字符流 继day35 应用场景&#xff1a;操作纯文本数据 注意&#xff1a;字符流 字节流编译器 编译器&#xff1a;可以识别中文字符和非中文字符&#xff0c;非中文字符获取1个字节&#xff08;一个字节一个字符&#xff09;&#xff0c;编译器会根据…

深入浅出Golang image库:编写高效的图像处理代码

深入浅出Golang image库&#xff1a;编写高效的图像处理代码 引言image库概览图像处理基础概念image库的主要组成和功能image接口图像格式的支持color模型 结论 图像的基本操作创建图像新图像的创建从文件加载图像 图像的保存与导出图像的颜色和像素处理绘制基本形状和文字 高级…

【开源社区】openEuler、openGauss、openHiTLS、MindSpore

【开源社区】openEuler、openGauss、openHiTLS、MindSpore 写在最前面开源社区参与和贡献的一般方式开源技术的需求和贡献方向 openEuler 社区&#xff1a;开源系统官方网站官方介绍贡献攻略开源技术需求 openGauss 社区&#xff1a;开源数据库官方网站官方介绍贡献攻略开源技术…

数据结构(三)----栈和队列

目录 一.栈 1.栈的基本概念 2.栈的基本操作 3.顺序栈的实现 •顺序栈的定义 •顺序栈的初始化 •进栈操作 •出栈操作 •读栈顶元素操作 •若使用另一种方式: 4.链栈的实现 •链栈的进栈操作 •链栈的出栈操作 •读栈顶元素 二.队列 1.队列的基本概念 2.队列的基…

使用Python实现K均值聚类算法

K均值&#xff08;K-Means&#xff09;算法是一种常用的聚类算法&#xff0c;它将数据集分成K个簇&#xff0c;每个簇的中心点代表该簇的质心&#xff0c;使得每个样本点到所属簇的质心的距离最小化。在本文中&#xff0c;我们将使用Python来实现一个基本的K均值聚类算法&#…

关于ABP 新增表,dbfirst模式

下面的代码是基于abp生成的项目&#xff0c;项目名&#xff1a;Store 1.在Domain结尾的项目中通过EF工具生成数据实体&#xff1a; Scaffold-DbContext Data Source服务器IP;Initial Catalog数据库;User Idsa;Password密码;EncryptFalse; Microsoft.EntityFrameworkCore.SqlS…

JVM常量池

JVM中的常量池主要有以下几个类别&#xff0c;它们各自在JVM中的位置随着JDK版本的演进而有所变化&#xff1a; Class文件常量池&#xff1a; 位置&#xff1a;存在于每个独立的.class文件中。这是编译期间生成的二进制文件的一部分&#xff0c;它包含了该类或接口的所有编译期…

Java基础_15集合及其方法

今天的内容 1.集合 1.集合【重点】 1.1为什么使用集合 集合和数组是一样的都是用来存储数据的&#xff01;&#xff01;&#xff01; 真实的开发的时候&#xff0c;使用的是集合不是数组&#xff0c;为啥&#xff1f; 数组存数据: ​ 1.数组的容量是固定的 ​ 2.数组封装的方法…

慢品人间烟火色,闲观万事岁月长

小女孩的衣柜里&#xff0c;怎能缺少一套别致的新中式穿搭&#xff1f;让我们的小公主在时尚与传统中寻找平衡 演绎属于自己的中国风魅力精致的小立领&#xff0c;淡淡的文艺复古气息 上衣系带设计&#xff0c;外加一层高透轻纱穿上身如亭亭玉立的大小姐整个人仿佛笼罩了一层…

CVP(ChatGPT、Vector Database和Prompt)

CVP实际上指的是ChatGPT、Vector Database和Prompt的结合&#xff0c;这是一种新型的技术栈&#xff0c;用于构建智能应用。 首先&#xff0c;我们来看这三个组成部分&#xff1a; ChatGPT&#xff1a;这是一个强大的语言模型&#xff0c;它能够理解并生成自然语言文本。Chat…

【docker】之linux写shell脚本备份线上数据库(备份为dump文件)

目录 1. SH文件1.1 SH文件示例1.2 文件解释1.3 .sh文件执行 2. 备份线上数据库的.sh文件2.1 文件命令解析 3. 命令执行4. 线下dump文件的恢复与备份 环境&#xff1a;linux容器&#xff1a;docker 1. SH文件 SH文件通常指的是 Shell 脚本文件&#xff0c;文件后缀名为.sh&…

IP查询在追踪网络攻击源头中的应用

随着网络攻击事件的不断增加&#xff0c;追踪攻击源头成为网络安全领域的重要任务之一。IP查询技术通过分析网络流量中的IP地址&#xff0c;可以帮助确定攻击的来源。本文将探讨IP查询在追踪网络攻击源头中的应用&#xff0c;包括其原理、方法以及实际案例分析。 IP地址查询&a…

比特币减半后 牛市爆发

作者&#xff1a;Arthur Hayes of Co-Founder of 100x 编译&#xff1a;Qin jin of ccvalue (以下内容仅代表作者个人观点&#xff0c;不应作为投资决策依据&#xff0c;也不应被视为参与投资交易的建议或意见&#xff09;。 Ping PingPing&#xff0c;我的手机发出的声音&…

Linux CentOS 安装 MySQL 服务教程

Linux CentOS 安装 MySQL 服务教程 1. 查看系统和GNU C库(glibc)版本信息 1.1 查询机器 glibc 版本信息 glibc&#xff0c;全名GNU C Library&#xff0c;是大多数Linux发行版中使用的C库&#xff0c;为系统和应用程序提供核心的API接口。在Linux系统中&#xff0c;特别是在…

js 数组 按列循环二维数组

期待效果&#xff1a; 核心代码&#xff1a; //js function handle(array) {var result [];for (let i 0; i < array[0].length; i) {var item []; for (let j 0; j < array.length; j) {item.push(array[j][i])} result.push(item);} return result; } 运行代码&a…

酷开科技 | 酷开系统打破客厅局限让大家享受更智能便捷的服务

曾经电视是家庭场景中的重要屏幕&#xff0c;是家庭发现世界和获取资讯的中心。但经历了近一百年的发展&#xff0c;传统电视行业进入瓶颈期&#xff0c;电视在家庭中的地位被逐渐替代。特别是智能手机的诞生&#xff0c;让手机成为个人生活的中心和全社会关注焦点&#xff0c;…

STM32F7 MPU Cache的原理和一致性问题

目录 概述 1 Cache 及其原理 2 结构及策略 2.1 Cache类型 2.2 Cache实例 3 Cache 及 MPU 属性 4 Cache 一致性问题 4.1 未Clean写Cache情况 4.2 DMA 更新物理内存情况 4.3 一个实例 4.3.1 功能描述 4.3.2 代码实现 4.3.3 结论 概述 本文主要从结构原理以及应用方…

接口自动化测试(python+pytest+requests)

一、选取自动化测试用例 优先级高:先实现业务流程用例、后实现单接口用例功能较稳定的接口优先开展测试用例脚本的实现二、搭建自动化测试环境 核心技术:编程语言:python;测试框架:pytest;接口请求:requests安装/验证requests:命令行终端分别输入 pip install requests / p…

gitlab 转移项目

git clone 旧项目url git remote rm origin git remote add origin 新的url git push origin master:master --force 报错 error: src refspec master does not match any error: failed to push some refs to xxx 查看当前分支 是否为master,如果为main,创建master分支 …

蓝桥杯day21刷题日记--接龙序列 动态规划

刚开始以为最长子序列的做法&#xff0c;然后发现数据太大了&#xff0c;只能得四十分&#xff0c;遂看题解&#xff0c;寻找AC做法 四十分做法 #include <iostream> #include <string> #include <algorithm> using namespace std; int dp[100010]; int n;…