生成12位短id,自增且不连续,永不重复,不依赖数据库

基本思路:

设计模式:单例模式

是否加锁:是 synchronized

获取最后一次生成的时间戳值T0

限定初始时间为2023-08-01 00:00:00,获取当前时间时间戳T1,T1与初始时间的毫秒差值T2,转为16进制,转为字符串为r1,获取该字符串的长度L1

获取L2 (length - L1) ,获取L2位数字的16进制自增数值范围,取最大值max

现数据库批量导入数据速度为 n条/ms

平均步长为max/n,(0~平均步长)的平均数为max/n/2,假设使用平均步长最为随机步长范围,最终的值与max相差较远,大约后一半的数字没有被使用

将平均步长*2-平均步长*容错因子(0.1)的值作为我们随机步长的范围  容错因子:减小溢出概率

随机步长step = max/n*2 - max/n*0.1

获取T1

如果T1 == T0,序列值seqNum = seqNum + step (转为16进制),若seqNum > max,该线程暂停1毫秒后刷新r1

如果T1 > T0,序列值seqNum = 0 + step

设置T0

代码实现如下:

/*** 生成短id* @author mayu*/
public class ShortIdWorker {/*** 初始时间限定为2023-08-01 00:00:00*/private final static long START_STAMP = 1690819200000L;/*** 容错因子*/private final static int FAULT_TOLERANCE_FACTOR = 10;/*** 默认长度*/private final static int DEFAULT_ID_LENGTH = 12;/*** 数据库每毫秒可保存的数据,结合列的数量取值,建议实测后更改*/private final static int DEFAULT_TRANSFER_SPEED_PER_MILLISECOND = 50;private final int length;private final int transferSpeedPerMillisecond;/*** 上次运行时间*/private long lastStamp = -1L;/*** 增长序列*/private int seqNum;private static ShortIdWorker instance;/*** 单例模式*/public static ShortIdWorker getInstance() {if (null == instance) {instance = new ShortIdWorker();}return instance;}public static ShortIdWorker newInstance(int length, int transferSpeedPerMillisecond) {return new ShortIdWorker(length, transferSpeedPerMillisecond);}/*** 默认使用12位id,数据库每毫秒新增数据为50条*/private ShortIdWorker() {this(DEFAULT_ID_LENGTH, DEFAULT_TRANSFER_SPEED_PER_MILLISECOND);}private ShortIdWorker(int length, int transferSpeedPerMillisecond) {this.length = length;this.transferSpeedPerMillisecond = transferSpeedPerMillisecond;}/*** @return 生成后的id* <p>* 例:757b12c001d3* 共length位id,前x位为时间戳差值的16进制,后y位为不固定步长的自增序列*/public synchronized String nextId() {long now = now();// 获取16进制时间戳前缀String stampPrefix = getStampStr(now);// 获取第二段增长序列的长度l2int l2 = this.length - stampPrefix.length();// 获取l2位16进制的最大值int max = IntStream.range(0, l2).map(i -> 16).reduce(1, (a, b) -> a * b) - 1;// 获取增长的平均步长averageStepLengthint averageStepLength = max / this.transferSpeedPerMillisecond;// 取步长范围// averageStepLength的平均值是averageStepLength/2,累加的情况下会有后一半的空间浪费问题,故取值为averageStepLength*2,平均值为averageStepLength// 取随机数的结果不可控,上行中列举的只是近似值,为防止多次溢出影响程序执行时间,再减去容错因子,减小溢出概率(容错因子建议在本地系统实测后更改)int randomStepLengthMax = (averageStepLength << 1) - (averageStepLength / FAULT_TOLERANCE_FACTOR);// 在步长范围内获取随机步长int randomStepLength = new Random().nextInt(randomStepLengthMax) + 1;// 当上次运行时间小于当前时间或第一次运行时,增长序列赋值为随机步长,设置最后运行时间if (this.lastStamp < now || this.lastStamp == -1L) {this.seqNum = randomStepLength;this.lastStamp = now;// 当上次运行时间与当前运行时间处于同一毫秒时} else if (this.lastStamp == now) {// 增长序列以随机步长为步长递增this.seqNum += randomStepLength;// 当增长序列大于最大值时if (this.seqNum > max) {// 程序暂停一毫秒LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));// 重新获取前缀,增长序列重新开始this.seqNum = randomStepLength;Long newNow = now();this.lastStamp = newNow;stampPrefix = getStampStr(newNow);}} else {// 时钟回拨,报错throw new IllegalStateException("Clock moved backwards.  Reject to generate id");}// 将增长序列转为16进制与时间戳拼接return stampPrefix + String.format("%0" + l2 + "X", new BigInteger(String.valueOf(this.seqNum), 10));}private String hex10To16(String str) {return String.format("%X", new BigInteger(str, 10));}private long now() {return System.currentTimeMillis();}/*** 获取传入时间与开始时间的间隔毫秒数,将结果转为16进制* @param now 时间戳* @return*/private String getStampStr(Long now) {return hex10To16(String.valueOf(now - START_STAMP));}

        8位16进制可使用到4201年-03-20 07:32:15,后续时间戳所占位数自动变为9位,id总长度不变,不用担心id用尽的问题。

        代码中关于时间赋值的代码请谨慎改动,顺序颠倒会产生bug。

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

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

相关文章

攻防世界-Hear-with-your-Eyes

原题 解题思路 是一个没有后缀的文件&#xff0c;题目提示要用眼睛看这段音频&#xff0c;notepad打开文件&#xff0c;没什么东西。 加后缀zip再解压看看。 使用Audacity打开音频文件

Mysql高阶语句(二)

一、设置别名&#xff08;alias ——>as&#xff09; 在 MySQL 查询时&#xff0c;当表的名字比较长或者表内某些字段比较长时&#xff0c;为了方便书写或者 多次使用相同的表&#xff0c;可以给字段列或表设置别名。使用的时候直接使用别名&#xff0c;简洁明了&#xff0…

面试被问到:测试计划和测试方案有什么区别?你会回答吗~

面试的时候&#xff0c;很多小伙伴都被面试官问过这个问题 “测试计划和测试方案有什么区别”&#xff1f; 到底有什么区别呢&#xff1f;我们先好好了解下这两个文档。 一、测试计划 1、测试计划是什么 测试计划是组织管理层面的文件&#xff0c;从组织管理的角度对一次测…

【已解决】oracle获取最近2学年的数据

已解决 &#xff1a;oracle获取最近2学年的数据 SELECT * FROM (SELECT * FROM xx.JWXT_XSKB WHERE AND xn IN (‘2023-2024’,‘2022-2023’)); 问题 某某系统课表数据过大&#xff0c;要求只获取最近2学年的数据&#xff0c;不能写死。 思路 mysql 在子查询的WHERE子句中…

存量时代的面经

spring-series,感谢这个项目的作者,还有springboot_learn 这个项目 Spring, 说来惭愧,接触spring 已经接近十年了 通过这个项目,我把spring重写的学习了一遍 1,早上先debug 的一个项目的源码是 springboot_security 在这里插入代码片/****: Creating filter chain:org…

大规模场景下对Istio的性能优化

简介 当前istio下发xDS使用的是全量下发策略&#xff0c;也就是网格里的所有sidecar(envoy)&#xff0c;内存里都会有整个网格内所有的服务发现数据。这样的结果是&#xff0c;每个sidecar内存都会随着网格规模增长而增长。 Aeraki-mesh aeraki-mesh项目下有一个子项目专门用来…

【数据结构初阶】一. 复杂度讲解

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 学C的第三十四天【程序环境和预处理】_高高的胖子的博客-CSDN博客 1 . 算法效率 &#xff08;1&#xff09;. 什么是数据结构&#xff1a; 数据结构(Data Structure)是计算机存储、…

STM32G030F6 (SOP-20)Cortex ® -M0+, 32KB Flash, 8KB RAM, 17 GPIOs

淘宝淘了一批 STM32G030F6P6 SOP20&#xff0e;先备注一下, 还没想到能干嘛用&#xff0e; 手上的 STM32F103C6T6还剩一些&#xff0e; 一堆 “淘宝原厂STM32F103C8T6”, 还烫着手. 理解信息: ( 逐步补充 ) System Clock GPIOs GPIOs 17 PA[7:0] : 8bits USART Timer ADC I2…

git 操作

merge操作 git checkout -b bugFix git commit //在bugFix branch上操作&#xff0c;并且commit git checkout main git commit //在main上操作&#xff0c;并且commit git merge bugFix //此时on main branch&#xff0c;如果在bugFix branch执行merge bugFix&#…

10881 - Piotr‘s Ants (UVA)

题目链接&#xff1a;Online Judge 根据刘汝佳的解法的思路&#xff0c;我的代码如下&#xff1a; #include <cstdio> #include <algorithm> #include <string> const int maxn 10001;struct ant{int id;int loc;int dir; };bool cmp(const ant &a, c…

springdoc-openapi-ui 整合 knife,多模块分组,脚手架

pom文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.o…

混合查询多家快递,快速掌握物流信息

在现代社会&#xff0c;快递服务已成为我们日常生活的重要组成部分。无论是购物还是文件传递&#xff0c;我们都需要快递服务的帮助。然而&#xff0c;不同的快递公司需要不同的查询方法&#xff0c;这无疑增加了我们的查询难度。因此&#xff0c;有没有一种方法可以让我们一次…

CVE-2023-3450:锐捷 RG-BCR860 命令执行漏洞复现

锐捷 RG-BCR860 命令执行漏洞(CVE-2023-3450)复现 0x01 前言 本次测试仅供学习使用&#xff0c;如若非法他用&#xff0c;与本文作者无关&#xff0c;需自行负责&#xff01;&#xff01;&#xff01; 0x02 漏洞描述 Ruijie Networks RG-BCR860是中国锐捷网络&#xff08;R…

【英文文章总结】数据管理指南系列:渐进式数据库设计

原文连接&#xff1a;系列https://martinfowler.com/data/https://martinfowler.com/data/ Evolutionary Database Design (martinfowler.com) 架构的进化。如何允许更改架构并轻松迁移现有数据&#xff1f; 如何应对项目的变动 迭代开发&#xff1a;很长一段时间人们把数据…

R语言列操作函数

目录 一.dplyr包 1.新增变量和变量重新赋值 2.筛选行 3.筛选列 4.分组计算 5.管道操作符 6.连接数据框 二.tidyr 1.列的分裂 2.列的合并 3.宽数据转长数据 4.长数据转宽数据 一.dplyr包 1.新增变量和变量重新赋值 > head(ToothGrowth)len supp dose 1 4.2 …

如何高效的解析Json?

Json介绍 Json是一种数据格式&#xff0c;广泛应用在需要数据交互的场景Json由键值对组成每一个键值对的key是字符串类型每一个键值对的value是值类型(boo1值数字值字符串值)Array类型object类型Json灵活性他可以不断嵌套&#xff0c;数组的每个元素还可以是数组或者键值对键值…

HDFS 集群动态节点管理

目录 一、动态扩容、节点上线 1.1 背景 1.2 扩容步骤 1.2.1 新机器基础环境准备 1.2.2 Hadoop 配置 1.2.3 手动启动 DataNode 进程 1.2.4 Web 页面查看情况 1.2.5 DataNode 负载均衡服务 二、动态缩容、节点下线 2.1 背景 2.2 缩容步骤 2.2.1 添加退役节点 …

/etc/ssh/sshd_config 配置文件中的 PasswordAuthentication PermitRootLogin 参数作用

1、PasswordAuthentication PasswordAuthentications是一种身份验证方式&#xff0c;通常用于远程服务器的登录。当用户连接到远程服务器并进行身份验证时&#xff0c;服务器会根据 用户名进行验证&#xff0c;如果验证成功允许用户访问服务器。在SSH中&#xff0c;PasswordAut…

补种胡杨树-滑动窗体

标题&#xff1a;补种未成活胡杨 | 时间限制&#xff1a;1秒 | 内存限制&#xff1a;262144K 近些年来&#xff0c;我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨&#xff08;编号1-N&#xff09;&#xff0c;排成一排。一个月后&#xff0c;有M棵胡杨未能成活。 现可补种胡杨…

Dijkstra的算法实现 -- 基于优先级队列

什么是Dijkstra算法&#xff1f; Shortest Path Tree (SPT) 单源最短路径算法 从指定节点出发&#xff0c;到达任何其他节点的距离最短。 和K算法、P算法的区别是&#xff1f; K算法、P算法&#xff0c;这两个算法都是最小生成树算法 Minimal Spanning Tree&#xff08;MST&am…