雪花算法-java

article/2025/9/19 20:44:18

前言
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:计算机基础专栏
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

在这里插入图片描述

目录

  • 一、初识雪花算法
  • 二、为什么需要分布式id
  • 三、手写雪花算法
    • 3.1实现时间戳
    • 3.2 代码分析
    • 3.3整体代码

一、初识雪花算法

雪花(snowflake):大自然中找不到几乎完全一样的雪花。

Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。

组成部分
在这里插入图片描述

  • 第1位占用1-bit,是符号位通常不使用。
  • 第2位开始的41-bit是时间戳。可表示2^41个数,每个数代表毫秒 可表示69年。
  • 中间的10-bit位可表示机器数,即2^10 = 1024台机器。
  • 最后12-bit位是自增序列,可表示2^12 = 4096个数。

二、为什么需要分布式id

在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。

在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需求;特别一点的如订单、骑手、优惠券也都需要有唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。

三、手写雪花算法

实现雪花算法主要要实现3个数字

  • now 生成id的时间戳
  • workId 机器编号id 一般分为 工作ID + 数据中心ID
  • n 序列号(同一时间内生成的第几个id从0开始)

步骤

  • 先获取当前毫秒时间戳
  • 比较当前时间戳和前一个时间戳
  • 如果时间戳相同 则序列号+1 如果不同 则序列号sequence=0 【注意范围】
  • 最后一次生成的时间戳更新为当前的时间戳。
  • 将当前时间戳左翼22位, 机器编码id左移12位 ,序列号用或运算拼接起来【左移 右边补0】在这里插入图片描述

3.1实现时间戳

获取指定时间的时间戳

public class MyGetTime {public static void get() throws ParseException {// 创建一个日期格式化对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置指定的日期时间String dateTime = "2023-04-01 0:0:00";// 将日期时间转换成Date对象Date date = sdf.parse(dateTime);// 获取指定日期时间的时间戳long timestamp = date.getTime();// 输出时间戳System.out.println(timestamp);}}

在这里插入图片描述

获取当前时间的时间戳

 public static void getNow(){long timeMillis = System.currentTimeMillis();// 输出时间戳System.out.println(timeMillis);}

在这里插入图片描述

3.2 代码分析

定义一些属性

  • 工作ID + 数据中心ID = 10bit的工作机器id
  • 毫秒内序列(0~4095)
  • 上次生成ID的时间截
    在这里插入图片描述
 /***     设置开始时间戳(13位)*     时间是 2023-04-01 0:0:00*/private final long twepoch = 1680278400000L;// 41-bit是时间戳    10-bit位可表示机器数   12-bit位是自增序列/*** 序列在id中占的位数*/private final long sequenceBits = 12L;// 机器数10位 一般=  机器id所占的位数+数据标识id所占的位数 一共10 bit/*** 机器id所占的位数*/private final long workerIdBits = 5L;/*** 数据标识id所占的位数*/private final long datacenterIdBits = 5L;/*** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/*** 支持的最大数据标识id,结果是31*/private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/*** 机器ID向左移12位*/private final long workerIdShift = sequenceBits;/*** 数据标识id向左移17位(12+5)*/private final long datacenterIdShift = sequenceBits + workerIdBits;/*** 时间截向左移22位(5+5+12)*/private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/*** 生成序列的掩码,这里为 4095 (0b111111111111=0xfff=4095)* 为了比较生成的序列号是否超出范围*/private final long sequenceMask = -1L ^ (-1L << sequenceBits);/*** 工作机器ID(0~31)*/private long workerId;/*** 数据中心ID(0~31)*/private long datacenterId;/*** 毫秒内序列(0~4095)*/private long sequence = 0L;/*** 上次生成ID的时间截*/private long lastTimestamp = -1L;

构造函数

/*** 构造函数** @param workerId     工作ID (0~31)* @param datacenterId 数据中心ID (0~31)*        工作ID  +   数据中心ID  = 10bit的工作机器id*/public SnowflakeDistributeId(long workerId, long datacenterId) {//maxWorkerId 是机器最大id 31if (workerId > maxWorkerId || workerId < 0) {//工作ID超过或最大值 或小于o  就抛出异常throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {//maxDatacenterId 数据标识id 最大值是 31throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}

获得下一个ID (该方法是线程安全的)
在这里插入图片描述

 /*** 获得下一个ID (该方法是线程安全的)** @return SnowflakeId*/public synchronized long nextId() throws InterruptedException {//获取当前时间戳long timestamp = System.currentTimeMillis();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳Thread.sleep(1);timestamp = System.currentTimeMillis();}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}

3.3整体代码

public class demo {// ==============================Fields===========================================/*** 开始时间截 (2015-01-01)*/private final long twepoch = 1420041600000L;/*** 机器id所占的位数*/private final long workerIdBits = 5L;/*** 数据标识id所占的位数*/private final long datacenterIdBits = 5L;/*** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/*** 支持的最大数据标识id,结果是31*/private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/*** 序列在id中占的位数*/private final long sequenceBits = 12L;/*** 机器ID向左移12位*/private final long workerIdShift = sequenceBits;/*** 数据标识id向左移17位(12+5)*/private final long datacenterIdShift = sequenceBits + workerIdBits;/*** 时间截向左移22位(5+5+12)*/private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/*** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)*/private final long sequenceMask = -1L ^ (-1L << sequenceBits);/*** 工作机器ID(0~31)*/private long workerId;/*** 数据中心ID(0~31)*/private long datacenterId;/*** 毫秒内序列(0~4095)*/private long sequence = 0L;/*** 上次生成ID的时间截*/private long lastTimestamp = -1L;//==============================Constructors=====================================/*** 构造函数** @param workerId     工作ID (0~31)* @param datacenterId 数据中心ID (0~31)*/public demo( long datacenterId,long workerId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)** @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳** @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间** @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}}

//阻塞到下一个毫秒,获得新的时间戳新增加了
tilNextMillis()、timeGen()方法

结果

  SnowflakeDistributeId snowflakeDistributeId = new SnowflakeDistributeId(0, 1);for (int i = 0; i < 10; i++) {long id = snowflakeDistributeId.nextId();System.out.println(id+"对应的二进制"+Long.toBinaryString(id));}

在这里插入图片描述

分析:

101001111111100010100001000000000001000000000000 可能想这也没有64位啊 这里 后12位是序列号 然后就是10位的工作机器的id 剩下的其实是当前时间戳与指定的开始时间戳的差值。
在这里插入图片描述
前面补0就行了。


http://chatgpt.dhexx.cn/article/niDeEw9a.shtml

相关文章

数据库中雪花算法是什么?

一、为何要用雪花算法 1、问题产生的背景 现如今越来越多的公司都在用分布式、微服务&#xff0c;那么对应的就会针对不同的服务进行数据库拆分&#xff0c;然后当数据量上来的时候也会进行分表&#xff0c;那么随之而来的就是分表以后id的问题。 例如之前单体项目中一个表中…

snowflake算法(雪花算法)

snowflake算法(雪花算法) 1.snowflake算法介绍 Snowflake算法产生是为了满足Twitter每秒上万条消息的请求&#xff0c;每条消息都必须分配一条唯一的id&#xff0c;这些id还需要一些大致的顺序&#xff08;方便客户端排序&#xff09;&#xff0c;并且在分布式系统中不同机器…

分布式ID生成算法——雪花算法

一、分布式ID ID可以唯一标识一条记录。 对于单体架构&#xff0c;我们可以使用自增ID来保证ID的唯一性。但是&#xff0c;在分布式系统中&#xff0c;简单的使用自增ID就会导致ID冲突。这也就引出了分布式ID问题。分布式ID也要求满足分布式系统的高性能、高可用、高并发的特点…

【SnowFlake】雪花算法(Java版本)

SnowFlake雪花算法&#xff08;Java版本&#xff09; 一、SnowFlake算法二、代码实现三、应用场景四、优缺点五、分布式生成ID方式 一、SnowFlake算法 雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法 Snowflake生成的是Long类型的ID&#xff0c;一个Long类型…

雪花算法以及具体实现

一、为何要用雪花算法 1、问题产生的背景 现如今越来越多的公司都在用分布式、微服务&#xff0c;那么对应的就会针对不同的服务进行数据库拆分&#xff0c;然后当数据量上来的时候也会进行分表&#xff0c;那么随之而来的就是分表以后id的问题。 例如之前单体项目中一个表中的…

什么是雪花算法,详解雪花算法原理

雪花算法(SnowFlake) 雪花算法是Twitter开源的分布式ID生成算法. 主要是由64bit的long型生成的全局ID,引入了时间戳和ID保持自增的属性. 64bit分为四个部分: 第一个部分是1bit, 这不 使用,没有意义; 第二个部分是41bit, 组成时间戳; 第三个部分是10bit, 工作机器ID,里面分为…

雪花算法原理及实现

背景 分布式高并发的环境下&#xff0c;最常见的就是每年双十一的十二点&#xff0c;大量用户同时抢购同一商品&#xff0c;毫秒级的时间下可能生成数万个订单&#xff0c;此时确保生成订单ID的唯一性变得至关重要。此外&#xff0c;在秒杀环境下&#xff0c;不仅要保障ID唯一…

雪花算法的原理和实现Java

SnowFlake 算法&#xff0c;是 Twitter 开源的分布式 id 生成算法。其核心思想就是&#xff1a;使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛&#xff0c;且ID 引入了时间戳&#xff0c;基本上保持自增的&#xff0c;后面的代码中有详细的注…

雪花算法简介

文章目录 1、简介2、雪花算法3、算法实现4、算法优缺点5、补充 1、简介 在生成随机主键的时候&#xff0c;我第一个想到的就是UUID&#xff0c;但是UUID在MySQL中作为主键生成时&#xff0c;就会出现一个问题&#xff0c;UUID生成的是乱序的。这时候&#xff0c;学习MP的过程中…

雪花算法

文章目录 1、雪花算法的起源2、雪花算法原理3、雪花算法java实现4、一些细节讨论4.1调整比特位分布4.2workerid一般如何生成 1、雪花算法的起源 snowflake中文的意思是 雪花&#xff0c;雪片&#xff0c;所以翻译成雪花算法。它最早是twitter内部使用的分布式环境下的唯一ID生…

SnowFlake 雪花算法详解与实现

我是陈皮&#xff0c;一个在互联网 Coding 的 ITer&#xff0c;个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新文章。 文章目录 背景SnowFlake 雪花算法算法实现算法验证算法优缺点注意事项 背景 现在的服务基本是分布式&#xff0c;微服务形式的&#xff0c;而且大数据…

雪花算法(SnowFlake)

简介 现在的服务基本是分布式、微服务形式的&#xff0c;而且大数据量也导致分库分表的产生&#xff0c;对于水平分表就需要保证表中 id 的全局唯一性。 对于 MySQL 而言&#xff0c;一个表中的主键 id 一般使用自增的方式&#xff0c;但是如果进行水平分表之后&#xff0c;多…

二维反卷积 matlab,二维反卷积的实现(实际意义不明确)

前言 一维反卷积(deconv),可以很好的实现一维卷积的反过程!但是二维反卷积就很难恢复了!为什么呢?因为我们知道二维卷积计算的过程就是:卷积核不断滑动,卷积核不断与原始数据中的小矩阵做"点乘并求和";现假设卷积核为3x3,那么每一个和它点乘的小矩阵对应尺寸…

python 反卷积(DeConv) tensorflow反卷积(DeConv)(实现原理+手写)

Tensorflow反卷积&#xff08;DeConv&#xff09;实现原理手写python代码实现反卷积&#xff08;DeConv&#xff09; 理解&#xff1a; https://www.zhihu.com/question/43609045/answer/130868981 上一篇文章已经介绍过卷积的实现&#xff0c;这篇文章我们学习反卷积原理&am…

地震信号系列完结篇-反卷积方法

前言 本篇将详细地讲解地震信号中用到的反卷积方法。反卷积方法的作用在文章 地震信号的一些基本概念 中已经阐述过&#xff0c;简单的说就是&#xff1a;在压缩原信号的同时&#xff0c;对频谱进行补偿&#xff08;反卷积的输出信号&#xff09;。而在地震信号处理中&#xf…

反卷积的棋盘格效应

本文译自来自谷歌大脑的AUGUSTUS ODENA等人的文章: Deconvolution and Checkerboard Artifacts[1], 虽然是16年的博客了, 但是其对解释反卷积的棋盘效应已经如何规避都给出了非常好和到位的意见. 下面让我们开始~ 前言 当我们分析由神经网络生成的图片的时候, 常常会发觉有一种…

反卷积神经网络介绍

反卷积是指&#xff1a;通过测量输出和已经输入重构未知输入的过程。在神经网络中&#xff0c;反卷积过程并不具备学习的能力&#xff0c;仅仅是用于可视化一个已经训练好的卷积网络模型&#xff0c;没有学习训练的过程。 下图所示为VGG 16反卷积神经网络的结构&#xff0c;展示…

一文读懂什么是反卷积

反卷积&#xff08;Deconvolution&#xff09;的概念第一次出现是Zeiler在2010年发表的论文Deconvolutional networks中&#xff0c;但是并没有指定反卷积这个名字&#xff0c;反卷积这个术语正式的使用是在其之后的工作中(Adaptive deconvolutional networks for mid and high…

反卷积相关论文理解

关于反卷积原理&#xff0c;小编就不再赘述&#xff0c;在知乎中有详细的解释&#xff0c;很清晰&#xff0c;都是大佬。 链接如下&#xff1a;https://www.zhihu.com/question/43609045/answer/120266511 反卷积相对于卷积在神经网络结构的正向和反向传播中做相反的运算&…

反卷积理解和推导

参考 怎样通俗易懂地解释反卷积&#xff1f; - 知乎&#xff0c;【基础知识学习】卷积与反卷积学习笔记 - 知乎 1.概念 反卷积是一种特殊的正向卷积&#xff0c;先按照一定的比例通过补 0 来扩大输入图像的尺寸&#xff0c;接着旋转卷积核&#xff0c;再进行正向卷积。 图1 反…