雪花算法简介以及代码实现

article/2025/9/19 20:15:43

文章目录

  • 雪花算法
    • 分布式ID
      • 分布式ID需要满足的要求
        • 全局唯一:
        • 高性能:
        • 高可用:
        • 方便易用:
        • 安全:
        • 有序递增:
        • 要求具体的业务含义:
        • 独立部署:
    • 组成
    • 代码实现
      • Java代码实现
      • Go语言实现

雪花算法

简介:

  • 雪花算法是Twitter开源的分布式ID生成算法 Github仓库地址

  • 雪花算法主要用于分布式系统中,数据库的ID生成

  • 在自然界中并不存在两片完全一样的雪花,每一片雪花都拥有自己漂亮独特的形状,独一无二.雪花算法也表示生成的分布式id如雪花般独一无二.

分布式ID

随着业务被使用的人越来越多, 单机的数据库已经很难保证业务能够流畅稳定的运行了, 这是我们需要对数据库进行分库分表存储, 使用分布式集群, 但是这样每个表的数据怎么保证ID唯一呢? 如果使用主键递增肯定发生ID不唯一的冲突情况, 所以急需一种可以生成全局唯一ID的算法来解决这个囧境.

img


分布式ID需要满足的要求

全局唯一:

  • 这是最基本的要求

高性能:

  • 不能为了全局唯一就去生成一大长串,肯定需要考虑性能,既要考虑生成的效率,又要考虑查询的效率(即存储的效率).

高可用:

  • 生成分布式ID的服务要保证可用性高,无限接近100%

方便易用:

  • 拿来即用,使用方便,快速接入!

安全:

  • 分布式ID中不应含有敏感信息,否则了解算法的不怀好意之人解码可能会获取到这些敏感信息.

有序递增:

  • 如果要把 ID 存放在数据库的话,ID 的有序性可以提升数据库写入速度。并且,很多时候 ,我们还很有可能会直接通过 ID 来进行排序。

要求具体的业务含义:

  • 生成的ID如果能有具体的业务含义,可以让定位问题以及开发更透明化(例如根据ID就能确定是哪个业务)

独立部署:

  • 也就是分布式系统单独有一个发号器服务,专门用来生成分布式 ID。这样就生成 ID 的服务可以和业务相关的服务解耦。不过,这样同样带来了网络调用消耗增加的问题。总的来说,如果需要用到分布式 ID 的场景比较多的话,独立部署的发号器服务还是很有必要的。(企业级的大型项目中十分有必要)

组成

雪花算法生成的ID是一个64bitlong型的数字且按时间趋势递增.大致有首位符号位(无效位), 时间戳插值, 机器编码, 序列号四部分组成.

img

如图:

  • 首位无效符: 主要用做为符号位,因为一般都是生成正数,所以符号位统一都是0
  • **时间戳:**占用41bit,精确到毫秒. 41bit位最好可以表示2^41-1毫秒, 转化成单位年为69年.
  • 机器编码: 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,最多可以容纳1024个节点.
  • **序列号:**占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到2^12-1,一共可以生成4096个ID(包括了0)

代码实现

提供了Java和Golang两种编程语言的实现方式

Java代码实现

snowFlake.java

package com.dyw.snowFlake;/*** @author Devil* @create 2022-04-04 14:25*/
@SuppressWarnings("all")
public class IDWorker {//十位的工作机器码private long workerId; //工作id 五位private long datacenterId; //数据中心id 五位//12位序列号private long sequence = 0L;//初始时间戳private final long twEpoch = 1288834974657L;//长度为5位private final long workerIdBits = 5L;private final long datacenterIdBits = 5L;//最大值private final long maxWorkerId = ~(-1L << workerIdBits);private final long maxDatacenterId = ~(-1L << datacenterIdBits);//序列号id长度private final long sequenceBits = 12L;private final long sequenceMask = ~(-1L << sequenceBits);//工作id需要左移的位数, 12位(序列号的位长)private final long workerIdShift = sequenceBits;//数据中心id需要左移的位数 序列号长+工作id长private final long datacenterIdShift = sequenceBits + workerIdBits;//时间戳左移位数 = 序列号长+工作id长+工作位长private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;//上次时间戳, 初始值位负值private long lastTimestamp = -1L;/*** 构造方法* @param workerId 工作节点id* @param datacenterId 数据中心id*/public IDWorker(long workerId, long datacenterId) {//检查参数的合法性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));}System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);this.workerId = workerId;this.datacenterId = datacenterId;}public long getWorkerId() {return workerId;}public long getDatacenterId() {return datacenterId;}public long getSequence() {return sequence;}/*** //下一个ID生成算法* @return snowflakeId*/public synchronized long nextId() {//先获取当前系统时间long timestamp = timeGen();//如果当前系统时间比上次获取id时间戳小就抛出异常 时钟往后移动可能会出现同样id所以这里必须抛异常结束执行if (timestamp < lastTimestamp) {System.err.printf("clock is moving backwards.  Rejecting requests until %d.",lastTimestamp);throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",lastTimestamp - timestamp));}//获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一,否则序列号赋值为0, 从零开始if(timestamp==lastTimestamp){//这是使用&sequenceMask是为了防止sequence溢出12位(前面要求了sequence的长度只能是12位)sequence = (sequence+1)&sequenceMask;//如果防止刚好移除经过&sequenceMask后 会变成0 可能会发生重复的情况//所以此时需要再次获取时间戳,并于上次时间戳作比较 直到与上次时间戳不一致返回当前时间戳避免重复if(sequence==0){timestamp = tilNextMillis(lastTimestamp);}}//如果不在同一个时间戳中 代表该序列刚开始计数所以初始为0else{sequence = 0;}//将上次时间戳值更新lastTimestamp = timestamp;/** 返回结果:* (timestamp - TwEpoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数* (datacenterId << datacenterIdShift) 表示将数据id左移相应位数* (workerId << workerIdShift) 表示将工作id左移相应位数* | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。* 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id*/return ((timestamp - twEpoch)<<timestampLeftShift) |(datacenterId<<datacenterIdShift) |(workerId<<workerIdShift)|sequence;}/*** 获取时间戳,并于上次时间戳作比较* @param lastTimestamp 上一次获取的时间戳* @return timestamp 更新后的系统时间*/private long tilNextMillis(long lastTimestamp){long timestamp = timeGen();while(timestamp<=lastTimestamp){timestamp = timeGen();}return timestamp;}/*** 获取系统时间戳* @return 系统时间戳*/private long timeGen() {return System.currentTimeMillis();}}

Go语言实现

snowFlake.go

package alimport ("errors""fmt""sync""time"
)var mutex = sync.Mutex{}const (//初始时间戳twEpoch int64 = 1288834974657//长度为5位workerIdBits     int64 = 5datacenterIdBits int64 = 5//最大值maxWorkerId     int64 = -1 ^ (-1 << workerIdBits)maxDatacenterId int64 = -1 ^ (-1 << datacenterIdBits)//序列号id长度sequenceBits int64 = 12sequenceMask       = -1 ^ (-1 << sequenceBits)//工作id需要左移的位数, 12位(序列号的位长)workerIdShift = sequenceBits//数据中心id需要左移的位数 序列号长+工作id长datacenterIdShift = sequenceBits + workerIdBits//时间戳左移位数 = 序列号长+工作id长+工作位长timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits
)//上次时间戳, 初始值位负值
var lastTimestamp int64 = -1type IDWorker struct {//十位的工作机器码workerId     int64 //工作id 五位datacenterId int64 //数据中心id 五位//12位序列号sequence int64
}func InitIDWorker(workerId, datacenterId int64) (*IDWorker, error) {//检查参数合法性if workerId > maxWorkerId || workerId < 0 {var err = errors.New(fmt.Sprintf("worker Id can't be greater than %d or less than 0", maxWorkerId))return nil, err}if datacenterId > maxDatacenterId || datacenterId < 0 {var err = errors.New(fmt.Sprintf("datacenter Id can't be greater than %d or less than 0", maxDatacenterId))return nil, err}fmt.Printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId)return &IDWorker{datacenterId: datacenterId,workerId:     workerId,}, nil
}/*下一个ID生成算法
*/
func (i *IDWorker) NextId() (id int64, err error) {//上锁mutex.Lock()//程序结束 释放锁defer mutex.Unlock()//先获取当前系统时间var timestamp = timeGen()//如果当前系统时间比上次获取id时间戳小就抛出异常 时钟往后移动可能会出现同样id所以这里必须抛异常结束执行if timestamp < lastTimestamp {fmt.Printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp)err = errors.New(fmt.Sprintf("Clock moved backwards.  Refusing to generate id for %d milliseconds",lastTimestamp-timestamp))}//获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一,否则序列号赋值为0, 从零开始if timestamp == lastTimestamp {//这是使用&sequenceMask是为了防止sequence溢出12位(前面要求了sequence的长度只能是12位)i.sequence = (i.sequence + 1) & sequenceMask//如果防止刚好移除经过&sequenceMask后 会变成0 可能会发生重复的情况//所以此时需要再次获取时间戳,并于上次时间戳作比较 直到与上次时间戳不一致返回当前时间戳避免重复if i.sequence == 0 {timestamp = tilNextMillis(lastTimestamp)}} else {//如果不在同一个时间戳中 代表该序列刚开始计数所以初始为0i.sequence = 0}//将上次时间戳值更新lastTimestamp = timestamp//返回雪花算法生成的idid = ((timestamp - twEpoch) << timestampLeftShift) |(i.datacenterId << datacenterIdShift) |(i.workerId << workerIdShift) |i.sequencereturn
}func (i IDWorker) WorkerId() int64 {return i.workerId
}
func (i IDWorker) DatacenterId() int64 {return i.datacenterId
}func (i IDWorker) Sequence() int64 {return i.sequence
}/*获取系统时间戳
*/
func timeGen() int64 {return time.Now().UnixMilli()
}/*获取时间戳,并于上次时间戳作比较
*/
func tilNextMillis(lastTimestamp int64) int64 {var timestamp = timeGen()for timestamp <= lastTimestamp {timestamp = timeGen()}return timestamp
}

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

相关文章

雪花算法(id生成算法)

SnowFlake 雪花算法 SnowFlake 中文意思为雪花&#xff0c;故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。 实现原理 雪花算法原理就是生成一个的64位比特位的 long 类型的唯一 id。 最高1位固定值0&#xff0c…

什么是雪花算法?啥原理?

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

雪花算法-java

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;计算机基础专栏 &#x1f4e7;如果文章知识点有错误的地方&#…

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

一、为何要用雪花算法 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;展示…