雪花算法改造: 兼容JS截短位数的53bit分布式ID生成器

一、基本介绍

雪花算法是一种生成分布式ID的算法。此种算法由Twitter创建,并应用于推文的ID。

一个SnowFlake有64位:

• 符号位(1) :正数0,负数1。一般生成的ID 都为正数,所以默认为0.

• 时间戳(41):表示毫秒值。

• 数据编码(5) + 机器编码(5):计算机ID,防冲突

• 序列号(12):每台机器生成的ID序列号。

理论上,当机器编码和数据编码不变的情况下,可以生成2^53个ID,达到千万亿级别。

image-20240415163134117

二、实现代码

public class SnowFlake {/*** 起始的时间戳*/private final static long START_STMP = 1480166465631L;/*** 每一部分占用的位数*/private final static long SEQUENCE_BIT = 12; //序列号占用的位数private final static long MACHINE_BIT = 5;  //机器标识占用的位数private final static long DATACENTER_BIT = 5;//数据中心占用的位数/*** 每一部分的最大值*/private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;private long datacenterId;  //数据中心private long machineId;    //机器标识private long sequence = 0L; //序列号private long lastStmp = -1L;//上一次时间戳public SnowFlake(long datacenterId, long machineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.datacenterId = datacenterId;this.machineId = machineId;}/*** 产生下一个ID** @return*/public synchronized long nextId() {long currStmp = getNewstmp();if (currStmp < lastStmp) {throw new RuntimeException("Clock moved backwards.  Refusing to generate id");}if (currStmp == lastStmp) {//相同毫秒内,序列号自增sequence = (sequence + 1) & MAX_SEQUENCE;//同一毫秒的序列数已经达到最大if (sequence == 0L) {currStmp = getNextMill();}} else {//不同毫秒内,序列号置为0sequence = 0L;}lastStmp = currStmp;return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分| datacenterId << DATACENTER_LEFT      //数据中心部分| machineId << MACHINE_LEFT            //机器标识部分| sequence;                            //序列号部分}private long getNextMill() {long mill = getNewstmp();while (mill <= lastStmp) {mill = getNewstmp();}return mill;}private long getNewstmp() {return System.currentTimeMillis();}public static void main(String[] args) {SnowFlake snowFlake = new SnowFlake(2, 3);for (int i = 0; i < (1 << 12); i++) {System.out.println(snowFlake.nextId());}}
}

三、扩展改造:兼容JS短位数的53bit分布式ID生成器

使用原生的雪花算法其默认生成的是64bit长整型, 如果以ID和前端的JS进行交互时会出现精度丢失(最后两位数字变成00) 而导致最终系统报错: 找不到ID。究其原因是因为JS的Number类型精度最高只有53bit, 导致JS其最大安全值只有2^53 = 9007199254740992 。雪花算法生成的18位数字也就会超标了;

解决方法: 将返回的ID由Long类型转换为String:

  • 使用@JsonSerialize注解,在相关类的属性上分别添加
  • 配置消息转换器MappingJackson2HttpMessageConverter,将此消息转换器进行扩展

由于上两种方案都不满足当前系统的要求(前端会进行计算),所以不能使用上两种方案。

主动适配前端JS的number类型的最大精度,将原来由雪花算法生成的64bit的ID截取为53bit的ID。
对于前后台传参Long类型而言,JS内置有32位整数,而number类型的安全整数是53位。如果超过53位,则精度会丢失。如果后台传来一个64位的Long型整数,因为超过了53位,所以后台返回的值和前台获取的值会不一样。

最后我们根据目前的业务数据量发现,53bit的ID足够当下的使用。所以我们尝试改造雪花算法,缩短其长度。

改造后的雪花算法组成部分:符号位(1)+ 时间戳(41)+机器编码(5)+ 序列号(7)

理论上,当机器编码不变的情况下可以生成0-2^53个ID,百万亿级别。

四、缩短版雪花算法

public class ShortenSnowFlake {/*** 起始的时间戳*/private final static long START_STMP = 1480166465631L;/*** 每一部分占用的位数*/private final static long SEQUENCE_BIT = 7; //序列号占用的位数private final static long MACHINE_BIT = 5;  //机器标识占用的位数/*** 每一部分的最大值*/private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT;private long machineId;    //机器标识private long sequence = 0L; //序列号private long lastStmp = -1L;//上一次时间戳public ShortenSnowFlake(long machineId) {if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.machineId = machineId;}/*** 产生下一个ID** @return*/public synchronized long nextId() {long currStmp = getNewstmp();if (currStmp < lastStmp) {throw new RuntimeException("Clock moved backwards.  Refusing to generate id");}if (currStmp == lastStmp) {//相同毫秒内,序列号自增sequence = (sequence + 1) & MAX_SEQUENCE;//同一毫秒的序列数已经达到最大if (sequence == 0L) {currStmp = getNextMill();}} else {//不同毫秒内,序列号置为0sequence = 0L;}lastStmp = currStmp;return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分| machineId << MACHINE_LEFT            //机器标识部分| sequence;                            //序列号部分}private long getNextMill() {long mill = getNewstmp();while (mill <= lastStmp) {mill = getNewstmp();}return mill;}private long getNewstmp() {return System.currentTimeMillis();}public static void main(String[] args) {ShortenSnowFlake snowFlake = new ShortenSnowFlake(3);for (int i = 0; i < (1 << 7); i++) {System.out.println(snowFlake.nextId());}}
}

五、项目配置

由于在不同的业务场景下需要的雪花算法位数也不同,为了更加灵活使用,我们采用配置的形式。

①Nacos新增配置

image-20240415205858649

②绑定文件中新增属性

image-20240415163354176

③自定义缩短版雪花算法生成器

public class ShortenSnowFlakeGenerator implements MyIdGenerator {private final ShortenSnowFlake shortenSnowFlake;/*** 构造函数。** @param workNode 工作节点。*/public ShortenSnowFlakeGenerator(Integer workNode) {shortenSnowFlake = new ShortenSnowFlake(workNode);}/*** 获取基于Snowflake算法的数值型Id。* 由于底层实现为synchronized方法,因此计算过程串行化,且线程安全。** @return 计算后的全局唯一Id。*/@Overridepublic long nextLongId() {return this.shortenSnowFlake.nextId();}/*** 获取基于Snowflake算法的字符串Id。* 由于底层实现为synchronized方法,因此计算过程串行化,且线程安全。** @return 计算后的全局唯一Id。*/@Overridepublic String nextStringId() {return this.shortenSnowFlake.nextIdStr();}
}

④改造初始化逻辑

image-20240415163531203

参考资料

• java 雪花算法,同时解决超过前端 js 数字上限的问题

• Twitter雪花算法SnowFlake改造: 兼容JS截短位数的53bit分布式ID生成器

• 分布式主键生成设计策略

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

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

相关文章

前端获取天气,及解决跨域问题

高德天气获取 访问高德官网&#xff0c;控制台&#xff0c;应用&#xff0c;创建新应用&#xff0c;高德开放平台 | 高德地图API (amap.com) 接口文档&#xff1a; 定位接口&#xff1a;IP定位-API文档| 高德地图API 天气预报接口&#xff1a;天气查询-API文档 | 高德地图AP…

文献学习-32-新生儿皮质表面重建的条件时间注意网络

Conditional Temporal Attention Networks for Neonatal Cortical Surface Reconstruction Authors: Qiang Ma, Liu Li, Vanessa Kyriakopoulou, Joseph V. Hajnal, Emma C. Robinson, Bernhard Kainz, and Daniel Rueckert Source: MICCAI 2023 Abstract 皮层表面重建在模拟…

WEB前端-用户注册倒计时

<body><textarea name"" id"" cols"30" rows"10">用户注册协议欢迎注册成为京东用户&#xff01;在您注册过程中&#xff0c;您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议&#xff0c;请您务必仔细阅读…

flink network buffer

Flink 的网络协议栈是组成 flink-runtime 模块的核心组件之一&#xff0c;是每个 Flink 作业的核心。它连接所有 TaskManager 的各个子任务(Subtask)&#xff0c;因此&#xff0c;对于 Flink 作业的性能包括吞吐与延迟都至关重要。与 TaskManager 和 JobManager 之间通过基于 A…

安卓官方例程

https://learn.microsoft.com/zh-cn/shows/connecton-demand/202?sourcerecommendations https://learn.microsoft.com/zh-cn/visualstudio/cross-platform/cross-platform-mobile-development-in-visual-studio?viewvs-2022 https://learn.microsoft.com/zh-cn/shows/xamari…

Python Flask Web 框架-API接口开发_4

一、1、安装 Falsk 当前用户安装 pip3 install --user Flask 确认安装成功&#xff1a; 进入python交互模式看下Flask的介绍和版本&#xff1a; $ python3>>> import flask >>> print(flask.__doc__)flask~~~~~A microframework based on Werkzeug. Its …

OpenCV基本图像处理操作(六)——直方图与模版匹配

直方图 cv2.calcHist(images,channels,mask,histSize,ranges) images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的…

电压比较器LM339介绍和仿真

电压比较器LM339介绍和仿真 &#x1f4d1;LM339相关特性 工作电源电压范围宽&#xff0c;单电源、双电源均可工作&#xff0c;单电源&#xff1a; 2&#xff5e;36V&#xff0c;双电源&#xff1a;1&#xff5e;18V&#xff1b;消耗电流小&#xff0c; Icc1.3mA&#xff1b;输…

计算机毕业设计springboot小区物业报修管理系统m8x57

该物业报修管理系统实施的目的在于帮助物业管理企业升级员工管理、住户管理、报修问题管理等内部管理平台&#xff0c;整合物业管理企业物力和人力&#xff0c;全面服务于维修人员管理的内部管理需求,并重视需求驱动、管理创新、与业主交流等外部需求,通过物业管理企业各项资源…

web前端js笔记

1&#xff0c;对象 let{ 属性 方法 } 2&#xff0c;闭包 只有函数内部的子函数才能读取局部变量&#xff0c;所以闭包可以理解成定义在一个函数内部的函数&#xff0c;在本质上&#xff0c;闭包是将函数内部和函数外部连接起来的桥梁。 3&#xff0c;math console.log(Math.flo…

学习笔记------时序约束之步骤

此篇记录FPGA的静态时序分析&#xff0c;在学习FPGA的过程中&#xff0c;越发觉得对于时序约束只是懂了个皮毛。现在记录一下自己的学习过程。 本文摘自《VIVADO从此开始》高亚军 在学习时序约束之前&#xff0c;先学习一下时序约束的步骤&#xff0c;方便后续查看一个整体的…

SETR——Rethinking系列工作,展示使用纯transformer在语义分割任务上是可行的,但需要很强的训练技巧

题目:Rethinking Semantic Segmentation from a Sequence-to-Sequence Perspective with Transformers 作者: 开源:https://fudan-zvg.github.io/SETR 1.研究背景 1.1 为什么要研究这个问题? 自[ 36 ]的开创性工作以来,现有的语义分割模型主要是**基于全卷积网络( FCN )的…