雪花算法(Snowflake)介绍和Java实现

1、雪花算法介绍

(1) 雪花算法(SnowFlake)是分布式微服务下生成全局唯一ID,并且可以做到去中心化的常用算法,最早是Twitter公司在其内部的分布式环境下生成ID的方式。 雪花算法的名字可以这么理解,世界上没有两片完全相同的雪花,而雪花算法希望自己生成的ID是独一无二的。

去中心化可以理解成不需要依赖某一个中间件,比如可以用Redis来生成全局唯一的ID,但Redis此时就属于中心,同时还会需要依赖网络。 而雪花算法通过10位bit的本地标识实现去中心化。

(2) 雪花算法生成的ID特点

  • 64bit位的正整数,即java中的long类型;
  • 整体结构是有序的。

(3) 64个bit位

  • 最高位:0, 代表是 一个正整数;
  • 41位:存储毫秒级的时间戳,在java中可以使用 System.currentMillons()获取,并且保证了自增特性;
  • 10位:存储机房/机器/操作系统/容器/服务的ID;
  • 12位:存储一个自增的序列。

说明:雪花算法内部的bit位数可以进行微调,比如5位机器id和5位服务id组合成10位。

2、Java方式实现雪花算法

(1)整体实现逻辑

64个bit位的long类型的值

第一位:占 1 个bit位,就是0

第二位:占 41 个bit位, 代表时间戳

第三位:占 5 个bit位, 代表机器id (这里将 10 bit 位 做了调整)

第四位:占 5 个bit位,服务id

第五位:占 12 个bit位, 自增序列

(2) 具备知识

  • java实现bit位移运算以及异或运算,用于计算固定bit位代表的最大数值,以及将bit位移动到固定位置。

例如:java中41个bit位的最大数值

long max41Bit = (1L << 41) - 1; // 41 bit 位 可以表示的最大数值 2的42次方减1, 即 1往左移41位减1

(3) 核心逻辑

  1. 先是定义5个位数对应的变量, 以及对应的偏移量,因为需要通过偏移后变量的bit位才能到达固定的位置;
  2. 再是计算自定义的机器id和服务id的最大值,用于严谨校验;
  3. 分别拿到对应的值,然后做位移运算;
  4. 做位移运算时的时间戳不从1900-01-01开始算起,因为41bit位的时间戳大概可用70年。

逻辑难点:在同一毫秒生成多个ID时,当前时间戳 与 自增序列的关系

  1. 拿到当前系统的毫秒值,记录生成上一个ID的毫秒值;
  2. 如果是同一毫秒生成ID,则自增序列递增(递增时要注意不能超出递增序列允许的最大值,超出需要等待下一毫秒);不同毫秒自增序列还原为初始值;
  3. 对于时针回拨问题,需要注意将当前的时间戳与生成的上一个ID的时间戳进行比较。

(4) Java代码具体实现


import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;/*** 雪花算法生成全局唯一的ID* 64个bit位的long类型的值* 第一位:占 1 个bit位,就是0* 第二位:占 41 个bit位, 代表时间戳* 第三位:占 5 个bit位, 代表机器id (这里将 10 bit 位 做了调整)* 第四位:占 5 个bit位,服务id* 第五位:占 12 个bit位, 自增序列*/
public class SnowFlakeUtil {/*** 41 个bit位存储时间戳, 从0开始计算, 最多可以存储 69.7年。* 如果从默认使用, 从1970年到现在,最多可以用到2040年。* 按照从 2023-12-28号开始计算,存储41个bit位, 最多可以使用到2093年*/private long timeStart = 1703692800000L;/*** 机器id, 通过yml配置的方式声明*/@Value("${snowflake.machineId:0}")private long machineId = 0;/*** 服务id, 通过yml配置的方式声明*/@Value("${snowflake.serviceId:0}")private long serviceId = 0;/*** 自增序列*/private long sequence;// 需要做机器id和服务id的兼容性校验, 不能超过了5位的最大值/*** 机器id占用的bit位数*/private long machineIdBits = 5L;/*** 服务id占用的bit位数*/private long serviceIdBits = 5L;/*** 序列占用的bit位数*/private long sequenceBits = 12L;/*** 计算出机器id的最大值 -1 往左移 machineIdBits 位, 再做亦或运算*/private long maxMachineId = -1 ^ (-1 << machineIdBits); // -1 往左移 machineIdBits 位, 再做亦或运算// 11111111 11111111 11111111 11111111 11111111// 11111111 11111111 11111111 11111111 11100000// 00000000 00000000 00000000 00000000 00011111/*** 计算出服务id的最大值*/private long maxServiceId = -1 ^ (-1 << serviceIdBits);/*** 校验 机器id 和 服务id 是否超过最大范围值*/@PostConstructpublic void init() {if (machineId > maxMachineId || serviceId > maxServiceId) {System.out.println("机器id或服务id超过最大范围值");}}/*** 服务id需要位移的位数, 即从右侧开始, 将数字左移 sequenceBits 到固定的位置*/private long serviceIdShift = sequenceBits;/*** 机器id需要位移的位数, 即从右侧开始, 将数字左移 sequenceBits + serviceIdBits  到固定的位置*/private long machineIdShift = sequenceBits + serviceIdBits;/*** 时间戳需要位移的位数, 即从右侧开始, 将数字左移 sequenceBits + serviceIdBits + machineIdBits 到固定的位置*/private long timestampShift = sequenceBits + serviceIdBits + machineIdBits;/*** 序列的最大值 -1 往左移 sequenceBits 位, 再做亦或运算*/private long maxSequenceId = -1 ^ (-1 << sequenceBits);/*** 记录最近一次获取id的时间*/private long lastTimestamp = -1;/*** 拿到当前系统时间的毫秒值** @return*/private long timeGen() {return System.currentTimeMillis();}/*** 生成全局唯一id* 因为有很多服务调用这个方法, 所以需要加sychronized锁*/public synchronized long nextId() {//1. 拿到当前系统时间的毫秒值long timestamp = timeGen();// 避免时间回拨造成出现重复的idif (timestamp < lastTimestamp) {// 说明出现了时间回拨System.out.println("当前服务出现时间回拨");}//2. 41个bit的时间知道了存什么了, 但是序列也需要计算一下。 如果是同一毫秒,序列就需要 还原 或者 ++// 判读当前生成的id的时间 和 上一次生成的时间if (timestamp == lastTimestamp) {// 同一毫秒值生成idsequence = (sequence + 1) & maxSequenceId; // 加1最大值进行与运算, 结果是如果超过了maxSequenceId则为0, 小于则不变if (sequence == 0) {// 进到这个if,说明已经超出了sequence序列的最大取值范围// 需要等到下一个毫秒值再回来生成具体的值timestamp = timeGen();// 写 <= 而不 写 == 是为了避免出现时间回拨的问题while (timestamp <= lastTimestamp) {// 时间还没动timestamp = timeGen();}}} else {// 另一个时间点生成idsequence = 0;}//3. 重新给 lastTimestamp 赋值lastTimestamp = timestamp;//4. 计算id,将几位值拼接起来, 41bit位的时间, 5位的机器, 5位的服务, 12位的序列return ((timestamp - timeStart) << timestampShift) | // 相减的差值 往左移  timestampShift(machineId << machineIdShift) |  // machineId 往左移  machineIdShift(serviceId << serviceIdShift) |  // serviceId 往左移  serviceIdShiftsequence &Long.MAX_VALUE;}
}

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

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

相关文章

用ChatGPT挑选钻石!著名珠宝商推出-珠宝GPT

根据Salesforce最新发布的第五版《互联网购物报告》显示&#xff0c;ChatGPT等生成式AI的出现、快速发展&#xff0c;对零售行业和购物者产生了较大影响。可有效简化业务流程实现降本增效&#xff0c;并改善购物体验。 著名珠宝商James Allen为了积极拥抱生成式AI全面提升销售…

Linux:apache优化(1)—— 长链接/保持连接

系统:CentOS 7.9 apache版本为&#xff1a;2.4.25 需要使用源码包进行安装才能够使用这些扩展模块 在使用这些扩展模块前要先下载zlib-devel 安装--enable-deflate选项需要的网页压缩传输的软件包 yum -y install zlib-devel 在配置编译安装时需要使用扩展配置 ./config…

PromQL语法

PromQL&#xff08;Prometheus Query Language&#xff09;是 Prometheus 内置的数据查询语言&#xff0c;它能实现对事件序列数据的查询、聚合、逻辑运算等。它被广泛应用在 Prometheus 的日常应用当中&#xff0c;包括对数据查询、可视化、告警处理当中。简单地说&#xff0c…

dev express 15.2图表绘制性能问题(dotnet绘图表)

dev express 15.2 绘制曲线 前端代码 <dxc:ChartControl Grid.Row"1"><dxc:XYDiagram2D EnableAxisXNavigation"True"><dxc:LineSeries2D x:Name"series" CrosshairLabelPattern"{}{A} : {V:F2}"/></dxc:XYDi…

Windows实现MySQL5.7主从复制(详细版)

使用免安装版本&#xff08;官网下载地址&#xff09; 在Windows上安装两种MySQL服务并同时开启服务 1.下载配置 打开解压文件所在位置&#xff0c;就新建一个配置文件my.ini。 2.主库安装 主库的my.ini配置文件如下&#xff1a; [mysqld] #设置主库端口&#xff0c;注意须是…

Leetcode—1572.矩阵对角线元素的和【简单】

2023每日刷题&#xff08;七十三&#xff09; Leetcode—1572.矩阵对角线元素的和 实现代码 class Solution { public:int diagonalSum(vector<vector<int>>& mat) {int n mat.size();if(n 1) {return mat[0][0];}int sum 0;int i 0, j n - 1;while(i &…

怎么解决 Nginx反向代理加载速度慢?

Nginx反向代理加载速度慢可能由多种原因引起&#xff0c;以下是一些可能的解决方法&#xff1a; 1&#xff0c;网络延迟&#xff1a; 检查目标服务器的网络状况&#xff0c;确保其网络连接正常。如果目标服务器位于不同的地理位置&#xff0c;可能会有较大的网络延迟。考虑使用…

\r\n和缓冲区/进度条小程序

一 前置知识 带有\n就会立马刷新缓冲区(因为显示器是行刷新)&#xff0c;\r不会刷新缓冲区 刷新的2个场景: 1 ~fflush 缓冲区中存在\r或\n --> \r fflush --> 不换行的\n) 2 ~ 文件关闭自动刷新缓冲区 倒计时小程序0-9 %-d是左对齐,%d是右对齐 倒计时小程序0-99 …

解决基于VectorGrid的矢量瓦片Y轴偏移的问题

目录 前言 一、GeoServer的瓦片 1、GeoWebcache缓存配置 2、矢量瓦片本地缓存 3、瓦片访问 二、VectorGrid加载本地瓦片 1、加载关键代码 2、默认模式的问题 3、问题分析 4、tms参数修改 总结 前言 在前面的博文介绍中&#xff0c;在线连接如下&#xff1a;浅谈前端自定义…

二叉树的后序遍历,力扣

目录 建议先刷一下中序遍历 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 注&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码实现&#xff08;递归&#xff09;&#xff1a; 代码实现&#x…

electron autoUpdater自动更新使用示例 客户端+服务端

封装好的 update.js 模块 use strict; const { autoUpdater } require(electron) // 更新检测 // https://www.electronjs.org/zh/docs/latest/api/auto-updaterconst checkUpdate (serverUrl) >{const updateUrl ${serverUrl}/update?platform${process.platform}&am…

pycharm python环境安装

目录 1.Python安装 2.PyQt5介绍 3.安装pyuic 4.启动designer.exe 5.pyinstaller(打包发布程序) 6.指定源安装 7.PyQt5-tools安装失败处理 8.控件介绍 9.错误记录 1.NameError: name reload is not defined 10.开发记录 重写报文输出和文件 ​编辑 1.Python安装 点…

burpsuite模块介绍之compare

导语 Burp Comparer是Burp Suite中的一个工具&#xff0c;主要提供一个可视化的差异比对功能&#xff0c;可以用于分析比较两次数据之间的区别。它的应用场景包括但不限于&#xff1a; 枚举用户名过程中&#xff0c;对比分析登陆成功和失败时&#xff0c;服务器端反馈结果的区…

java浅拷贝BeanUtils.copyProperties引发的RPC异常 | 京东物流技术团队

背景 近期参与了一个攻坚项目&#xff0c;前期因为其他流程原因&#xff0c;测试时间已经耽搁了好几天了&#xff0c;本以为已经解决了卡点&#xff0c;后续流程应该顺顺利利的&#xff0c;没想到 人在地铁上&#xff0c;bug从咚咚来~ 没有任何修改的服务接口&#xff0c;抛出…

Apache SSI 远程命令执行漏洞

一、环境搭建 二、访问upload.php 三、写shell <!--#exec cmd"id" --> 四、访问 如图所示&#xff0c;即getshell成功&#xff01;​

设计模式Java向

设计原则&#xff1a; 开闭原则&#xff1a; 用例对象和提供抽象功能进行分割&#xff0c;用例不变&#xff0c;抽象功能被实现&#xff0c;用于不断的扩展&#xff0c;于是源代码不需要进行修改&#xff0c;只在原有基础上进行抽象功能的实现从而进行代码扩展。不变源于代码…

java美容管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web美容管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

观察者模式概述

观察者模式,它用于建立一种对象与对象之间的依赖关系&#xff0c; 一个对象发生改变将自动通知其他对象&#xff0c; 其他对象将相应做出反应。在观察者模式种&#xff0c;发生改变的对象称为观察目标&#xff0c; 而被通知的对象称为观察者&#xff0c;一个观察目标可以对应多…

【Swagger】常用注解的使用、SpringBoot的整合及生产环境下屏蔽Swagger

一、引言 1、什么是Swagger&#xff1f; Swagger是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化RESTful风格的Web服务。它使得部署管理和使用功能强大的API从未如此简单。Swagger让文件的方法、参数和模型紧密集成到服务器端的代码&#xff0c;允许API始终保…

Python序列之集合

系列文章目录 Python序列之列表Python序列之元组Python序列之字典Python序列之集合&#xff08;本篇文章&#xff09; Python序列之集合 系列文章目录前言一、集合是什么&#xff1f;二、集合的操作1.集合的创建&#xff08;1&#xff09;使用{}创建&#xff08;2&#xff09;…