聊聊雪花算法?

news/2024/5/15 5:39:50/文章来源:https://blog.csdn.net/qq_42582773/article/details/128087284

随便聊聊

哈喽,大家好,最近换了份工作,虽然后端技术栈是老了点,但是呢,这边的前端技术确是现在市面上最新的那一套技术:Vue3+Vite+TSX+Pina+Element-Plus+NativeUI。我本人主要是学后端的,确被拉去做前端。唉(人生无常,大肠包小肠!)

但是呢,最近经常听到同事们一直在聊雪花算法,下面来,我们来聊聊雪花算法,是的

分布式ID

聊之前先说一下什么是分布式ID,抛砖引玉

假设现在有一个订单系统被部署在了A、B两个节点上,那么如何在这两个节点上各自生成订单ID,且ID值不能重复呢?

即在分布式系统中,如何在各个不同的服务器上产生唯一的ID值?

通常有以下三种方案:

  • 利用数据库的自增特性,不同节点直接使用相同数据库的自增ID;
  • 利用UUID算法产生ID值;
  • 使用雪花算法产生ID值

虽然Java提供了对UUID的支持,使用UUID.randomUUID()即可,但是由于UUID是一串随机的36位字符串,由32个数字和字母混合的字符串和4个“-”组成,长度过长且业务可读性差,无法有序递增,所以一般不用,更多使用的是雪花算法

由来

为什么叫雪花算法?

雪花算法的由来有两种说法:

  • 第一种,Twitter使用scala语言开源了一种分布式id生成算法—SnowFlake算法,被翻译成了雪花算法;
  • 第二种,因为自然界中并不存在两片完全一样的雪花的,每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。

组成

雪花算法生成的ID到底长啥样?

雪花算法生成的ID是一个64bit的long型的数字且按时间趋势递增。大致由首位无效符、时间戳差值、机器编码,序列号四部分组成。
在这里插入图片描述
如图:

  • 首位无效符:第一个bit作为符号位,因为我们生成的都是正数,所以第一个bit统一都是0;
  • 时间戳:占用41bit,精确到毫秒。41位最好可以表示2^41-1毫秒,转换成单位年为69年;
  • 机器编码:占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点;
  • 序列号:占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID

代码

/*** 雪花算法* @author Fang Ruichuan* @date 2022-11-28 21:24*/
public class SnowFlake {private long workerId;private long dataCenterId;// 每毫秒生产的序列号之从0开始递增private long sequence = 0L;// 1288834974657L是1970-01-01 00:00:00到2010年11月04日01:42:54所经过的毫秒数;// 因为现在二十一世纪的某一时刻减去1288834974657L的值,正好在2^41内。// 因此1288834974657L实际上就是为了让时间戳正好在2^41内而凑出来的。// 简言之,1288834974657L(即1970-01-01 00:00:00),就是在计算时间戳时用到的“起始时间”。private long twePoch = 1288834974657L;private long workerIdBits = 5L;private long datacenterIdBits = 5L;private long maxWorkerId = -1L ^ (-1L << workerIdBits);private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private long sequenceBits = 12L;private long workerIdShift = sequenceBits;private long datacenterIdShift = sequenceBits + workerIdBits;private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private long sequenceMask = -1L ^ (-1L << sequenceBits);private long lastTimestamp = -1L;public SnowFlake(long datacenterId, long workerId) {if ((datacenterId > maxDatacenterId || datacenterId < 0)|| (workerId > maxWorkerId || workerId < 0)) {throw new IllegalArgumentException("datacenterId/workerId值非法");}this.dataCenterId = datacenterId;this.workerId = workerId;}// 通过SnowFlake生成id的核心算法public synchronized long nextId() {long timestamp = Clock.systemUTC().millis();if (timestamp < lastTimestamp) {throw new RuntimeException("时间戳值非法");}// 如果此次生成id的时间戳,与上次的时间戳相同,就通过机器码和序列号区// 分id值(机器码已通过构造方法传入)if (lastTimestamp == timestamp) {/*下一条语句的作用是:通过位运算保证sequence不会超出序列号所能容纳的最大值。例如,本程序产生的12位sequence值依次是:1、2、3、4、...、4094、4095(4095是2的12次方的最大值,也是本sequence的最大值)那么此时如果再增加一个sequence值(即sequence + 1),下条语句就会使sequence恢复到0。即如果sequence==0,就表示sequence已满。*/sequence = (sequence + 1) & sequenceMask;// 如果sequencce已满,就无法再通过sequence区分id值:因此需要切换到if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {// 如果此次生成id的时间戳,与上次的时间戳不同,就已经可以根据时间戳区分id值sequence = 0L;}// 更新最近一次次生成id的时间戳lastTimestamp = timestamp;/**** 假设此刻的值是(二进制表示):*                 41位时间戳的值是:00101011110101011101011101010101111101011*                 5位datacenterId(机器码的前5位)的值是:01101*                 5位workerId(机器码的后5位)的值是:11001*                 sequence的值是:01001*             那么最终生成的id值,就需要:*                 1.将41位时间戳左移动22位(即移动到snowflake值中时间戳应该出现的位置);*                 2.将5位datacenterId向左移动17位,并将5位workerId向左移动12位*                 (即移动到snowflake值中机器码应该出现的位置);*                 3.sequence本来就在最低位,因此不需要移动。*             以下<<和|运算,实际就是将时间戳、机器码和序列号移动到snowflake中相应的位置。* @return long*/return ((timestamp - twePoch) << timestampLeftShift)| (dataCenterId << datacenterIdShift) | (workerId << workerIdShift)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = Clock.systemUTC().millis();// 如果当时时刻的时间戳 <= 上一次生成id的时间戳,就重新生成当前时间;// 即确保当前时刻的时间戳,与上一次的时间戳不会重复while (timestamp <= lastTimestamp) {timestamp = Clock.systemUTC().millis();}return timestamp;}// 测试1秒能够生成的id个数public static void generateIdsInOneSecond() {SnowFlake idWorker = new SnowFlake(1, 1);long start = Clock.systemUTC().millis();int i = 0;for (; Clock.systemUTC().millis() - start < 1000; i++) {idWorker.nextId();}long end = Clock.systemUTC().millis();System.out.println("耗时:" + (end - start));System.out.println("生成id个数:" + i);}public static void main(String[] args) {generateIdsInOneSecond();}
}

测试结果:

耗时:1000
生成id个数:4082481

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

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

相关文章

【博客545】从交换机视角看四种报文:广播、组播、未知单播、已知单播

从交换机视角看四种报文&#xff1a;广播、组播、未知单播、已知单播 交换机视角的四种报文 对于二层交换机来说&#xff0c;它在转发报文时&#xff0c;只有四种类型的报文&#xff1a; 1、广播 2、组播 3、未知单播 4、已知单播。四种报文剖析 1、二层广播报文 当二层交换…

SignalR简介及实践指南

SigalR简介 ASP.NET Core SignalR 是一个开放源代码库&#xff0c;可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。 适合 SignalR 的候选项&#xff1a; 需要从服务器进行高频率更新的应用。 示例包括游戏、社交网络、投票、拍卖…

易观千帆 | 2022年10月银行APP月活跃用户规模盘点

易观分析&#xff1a;易观千帆数据显示&#xff0c;10月手机银行服务应用活跃人数52285.79万&#xff0c;环比下降3.52%。手机银行服务应用月活规模经历了连续5个月的持续增长后&#xff0c;10月出现下降。 10月城商行手机银行服务应用活跃人数3565.56万&#xff0c;环比下降2…

UNIAPP实战项目笔记46 订单确认页面的布局

UNIAPP实战项目笔记46 订单确认页面的布局 实际案例图片 订单页面 具体内容图片自己替换哈&#xff0c;随便找了个图片的做示例 具体位置见目录结构 完善布局页面和样式 代码 confirm-order.vue部分 confirm-order.vue 确认订单页面布局和渲染 flex 样式布局 <template>…

字符串5:剑指Offer58-II.左旋转字符串

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;344. 反转字符串 题目&#xff1a; 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个…

衡师11月月赛web题目wp

目录 1.丢三落四的学姐 2.wep&#xff1f;Pwn&#xff01;&#xff01;&#xff01; 这题web部分是buuctf中的DASCTF X GFCTF 2022十月挑战赛&#xff01;的原题 1.丢三落四的学姐 访问题目位置&#xff0c;很明显的phpstudy搭建的痕迹 访问一下经常信息泄露的几个文件&…

Baklib|知识库应用场景:制作员工培训手册

持续的专业发展对于想要加入、保留和提升员工的组织来说是必不可少的。为了确保员工总是能从学习能力中受益&#xff0c;您需要考虑创建培训手册&#xff0c;使员工能够胜任并保持他们的工作能力。 在过去&#xff0c;您可能认为培训手册是一本厚重的册子&#xff0c;充满了密…

一文彻底搞懂Mysql索引优化

专属小彩蛋&#xff1a;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff08;前言 - 床长人工智能教程&#xff09; 目录 一、索引介绍 二、性能分析 三、查询优化 一、索引介绍…

Oracle中ALTER TABLE的五种用法(三)

首发微信公众号&#xff1a;SQL数据库运维 原文链接&#xff1a;https://mp.weixin.qq.com/s?__bizMzI1NTQyNzg3MQ&mid2247485212&idx1&sn450e9e94fa709b5eeff0de371c62072b&chksmea37536cdd40da7a94e165ce4b4c6e70fb1360d51bed4b3566eee438b587fa231315d0a5a…

通俗易懂的java设计模式(1)-单例模式

什么是单例模式&#xff1f; 单例模式是java中最简单的一种设计模式 需要注意的问题&#xff1a; 1.单例类有且只能有一个实例 2.单例类必须自己创建出这个实例&#xff0c;并提供给外界 那么如何自己创建实例而不让外界创建呢&#xff1f;很简单&#xff0c;我们将无参的构造函…

传输线理论基础01——相关定义、信号速率、分布参数与电报方程

前言一直以来都对高频信号、信号完整性、传输线、分布参数这些概念似懂非懂&#xff0c;上学时没学过相关课程&#xff0c;这导致我对高频电路和PCB理解较差&#xff0c;这里新开一个专栏&#xff0c;补齐这方面知识。 一. 传输线相关定义1.1 传输线定义 传输线指的是传输信号…

【Hack The Box】Linux练习-- Seventeen

HTB 学习笔记 【Hack The Box】Linux练习-- Seventeen &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年9月7日&#x1f334; &#x1f…

【数据去噪】SG-多项式平滑算法

文章目录一、简介二、原理5点3次多项式平滑三、代码1. 3点线性平滑2. 5点线性平滑3. 5点2次线性平滑4. 5点3次线性平滑5. 7点线性平滑6. 7点2次线性平滑一、简介 在处理工业数据的时候&#xff0c;工业数据有数据颗粒细&#xff0c;噪声大&#xff0c;量大&#xff0c;随着测量…

拿捏Fiddler抓包教程(10)-Fiddler如何设置捕获Firefox浏览器的Https会话

1.简介 经过上一篇对Fiddler的配置后&#xff0c;绝大多数的Https的会话&#xff0c;我们可以成功捕获抓取到&#xff0c;但是有些版本的Firefox浏览器仍然是捕获不到其的Https会话&#xff0c;需要我们更进一步的配置才能捕获到会话进行抓包。 2.宏哥环境 1.宏哥的环境是Win…

【Python】四、程序顺序和分支控制结构

文章目录实验目的一、赋值语句应用练习二、if- else分支结构三、多分支选择结构四、选择嵌套五、编程实现百分制换成五级制(优,良,中,及格,不及格)1.设计思路2.设计算法3.参考代码4.实验截图实验目的 掌握顺序和分支结构&#xff1b;培养学生动手查阅资料能力和解决实际问题的能…

CAD特殊符号,你不一定会

在CAD软件中&#xff0c;有时候会输入一些特殊的符号。比如在标明高低差的时候会输入“”号&#xff0c;在标明管子或者钢筋的直径为输入直径符号“”&#xff0c;为了标明角度值需要输入符号“”&#xff0c;那么这些符号怎么快速的绘制出来呢&#xff1f;我们一起用CAD梦想画…

初识计算机网络

目录 网络的发展 重新看待计算机结构 大型存储平台 认识 "协议" 网络和OS之间的关系 初识网络协议 协议分层 OSI七层模型 TCP/IP五层(或四层)模型 网络传输基本流程 局域网通信的原理 如果进行跨网络传输 网络通信里面的基本轮廓 数据包封装和分用…

QFile(文件)

QFile QFile提供一个用于读/写的接口&#xff0c;是一个可以用来读/写二进制文件的Qt资源的I/O设备&#xff0c;QFile可以单独使用&#xff0c;一般配合QTextStream或QDataStream 输入文件路径时最好使用"/"作为分隔符 构造函数&#xff1a; 常用的函数&#xff1a;…

Terraform 华为云最佳实践

目录划分如下&#xff1a;首先是环境&#xff0c;分为网络和service。global是全局的配置&#xff0c;也就是backend的配置&#xff0c;这次使用s3的存储作为backend的存储。最后就是模块做了一些封装。 在global里面的backend里面的main.tf去创建s3的存储。华为云支持s3存储&a…

四、nginx负载均衡[轮询]

一、负载均衡 解释&#xff1a;负载均衡分为两部分&#xff08;应用集群和负载均衡器&#xff09;。应用集群&#xff1a;将同一应用部署到多台机器上&#xff0c;组成处理集群&#xff0c;接收负载均衡设备分发的请求&#xff0c;进行处理并返回响应的数据。负载均衡器:将用户…