首页 > 编程语言 >Java - SnowflakeIdWorker 分布式全局唯一Id生成方案

Java - SnowflakeIdWorker 分布式全局唯一Id生成方案

时间:2024-12-31 14:13:36浏览次数:1  
标签:Java id private ID 毫秒 ------------- long SnowflakeIdWorker Id

Java - SnowflakeIdWorker 分布式全局唯一Id生成方案| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission |

| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------| -------------|
| 14762554| Java - SnowflakeIdWorker 分布式全局唯一Id生成方案| 2021-05-12T23:29:00| | BlogPost|

使用 Twitter的snowflake算法方案
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看:https://github.com/twitter/snowflake

具体实现

package com.demon.common.utils.gen;

import org.apache.commons.lang3.RandomUtils;

import java.util.Date;

/**

  • Twitter_Snowflake<br>

  • SnowFlake的结构如下(每部分用-分开):<br>

  • 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>

  • 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>

  • 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)

  • 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>

  • 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>

  • 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>

  • 加起来刚好64位,为一个Long型。<br>

  • SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

  • @author Demon-HY

  • 为方便使用,只需要在同集群应用内将机器标识出不同的编号即可

  • @date 2019-7-30
    */
    public class SnowflakeIdWorker {

    private static SnowflakeIdWorker idWorker;

    // TODO 部署多台服务,这里可以交给 Spring 管理,注入workerId
    static {
    idWorker = new SnowflakeIdWorker(1);
    }

    / 开始时间截 (1980-01-01) FIXME 保证生成的编号有13位*/
    // private static final long twepoch = 315504000000L;
    /
    开始时间截 (2019-07-01) */
    private static final long twepoch = 1561910400000L;

    /**

    • 时间位取&
      */
      private static final long timeBit = 0b1111111111111111111111111111111111111111110000000000000000000000L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 10L;

    /** 数据标识id所占的位数 */
    private final long datacenterIdBits = 0L;

    /** 支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 支持的最大数据标识id,结果是0 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 数据标识id向左移22位(12+10) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /** 时间截向左移22位(10+0+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~1023) */
    private long workerId;

    /** 数据中心ID(0) */
    private long datacenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence;

    private long initSequence;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    //Constructors=======

    /**

    • 构造函数
    • @param workerId 工作ID (0~1023)
      */
      private SnowflakeIdWorker(long workerId) {
      this(workerId,0);
      }
      /**
    • 构造函数
    • @param workerId 工作ID (0~1023)
    • @param datacenterId 数据中心ID (0)
      */
      private SnowflakeIdWorker(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));
      }
      this.workerId = workerId;
      this.datacenterId = 0;
      //随机生成2000种可能
      this.initSequence=RandomUtils.nextInt(0,2000);
      this.sequence=initSequence;
      //FIXME 1ms内有2000种可猜测
      }

    // Methods============
    /**

    • 获得下一个ID (该方法是线程安全的)

    • @return SnowflakeId
      */
      private 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 = initSequence;
      }

      //上次生成ID的时间截
      lastTimestamp = timestamp;

      //移位并通过或运算拼到一起组成64位的ID
      return ((timestamp - twepoch) << timestampLeftShift) //
      | (datacenterId << datacenterIdShift) //
      | (workerId << workerIdShift) //
      | sequence;
      }

    /**

    • 从ID中获取时间
    • @param id 由此类生成的ID
      */
      public static Date getTime(long id){
      return new Date(((timeBit&id)>>22)+twepoch);
      }

    /**

    • 阻塞到下一个毫秒,直到获得新的时间戳
    • @param lastTimestamp 上次生成ID的时间截
    • @return 当前时间戳
      */
      private long tilNextMillis(long lastTimestamp) {
      long timestamp = timeGen();
      while (timestamp <= lastTimestamp) {
      timestamp = timeGen();
      }
      return timestamp;
      }

    /**

    • 返回以毫秒为单位的当前时间
    • @return 当前时间(毫秒)
      */
      private long timeGen() {
      return System.currentTimeMillis();
      }

    /**

    • 获取唯一ID
      */
      public static Long getId() {
      return idWorker.nextId();
      }

    /**

    • 获取随机字符串,length=13
      */
      public static String getRandomStr() {
      return Long.toString(idWorker.nextId(), Character.MAX_RADIX);
      }

    //Test===============
    /** 测试 */
    public static void main(String[] args) {
    for (int i = 0; i < 100; i++) {
    long id = SnowflakeIdWorker.getId();
    String strId = SnowflakeIdWorker.getRandomStr();

         System.out.println(id);
         System.out.println(strId);
         System.out.println(SnowflakeIdWorker.getTime(id));
     }
    

    }
    }
    ————————————————
    版权声明:本文为CSDN博主「Demon-HY」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u014186972/article/details/97799211

 

| 648658| | 2021-05-12T23:29:00| false| | 2021-05-12T23:28:56.337| true| 使用 Twitter的snowflake算法方案snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒| Anonymous|

标签:Java,id,private,ID,毫秒,-------------,long,SnowflakeIdWorker,Id
From: https://www.cnblogs.com/ralphlauren/p/18621212

相关文章

  • JavaSpring AI与阿里云通义大模型的集成使用Java Data Science Library(JDSL)进行数据处
    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站学习总结1、掌握JAVA入门到进阶知识(持续写作中……)2、学会Oracle数据库入门到入土用法(创作中……)3、手把手教你开发炫酷的vbs脚本制作(完善中……)4、牛逼哄哄的IDEA......
  • Midjourney 官方用户端更新 · 基础介绍 · 多重个性化配置文件与情绪版
    MidJourney又更新了,更新了啥呐?官方称之为“多重个性化配置文件与情绪板”,在我写这个开头的时候,已经测试了一下午,一直考虑要不要写这篇,这个对新手不友好,且这个个性化基础架构还是早期版本…最后想了想还是有必须要写的,因为根据这几天更新的东西,我隐隐有些猜测,世界工具+个......
  • 关于 IntelliJ IDEA 2024 安装激活使用教程以及常见问题(激活至2026,实际上永久,亲测!)
    申明:本教程IntelliJIDEA补丁、激活码均收集于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除。若条件允许,希望大家购买正版!卸载老版本IDEA首先,如果小伙伴的电脑上有安装老版本的IDEA,需要将其彻底卸载掉,如下所示(没有安装则不用管,直接安装即可):TIP:如果你之前使......
  • Android RadioButton与container左侧padding调整
    遇到了这个问题:Android:Setmargintoleftsideofradiobuttondrawable同时在这个情景下并不能修改controller内容,希望通过xml达到这个效果。一般是通过添加android:paddingLeft来达到效果,但在RadioButton中它修改的是radio和text之间的距离。另一种方法是在外面套一个Linea......
  • 图书管理小程序|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 物流管理小程序|Java|SpringBoot|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SpringBoot、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apache......
  • 小演员招募小程序|Java|SpringBoot|VUE| 前后端分离
                              【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SpringBoot、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具......
  • 农产品商城小程序|Java|SpringBoot|VUE| 前后端分离
                              【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SpringBoot、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具......
  • Android Studio接口对接部署
    1.配置请求BaseUrl地址建立BaseHelper.ktobjectRetrofitInstance{privateconstvalBASE_URL="https://xxxxxxx.com/"valretrofit:Retrofit=Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.creat......
  • 【Java项目】基于SpringBoot+Vue的嗨玩旅游网站的设计与实现(源码+LW+包运行)
    源码获取:https://download.csdn.net/download/u011832806/89756183基于SpringBoot+Vue的嗨玩旅游网站开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven嗨玩旅游网站是一个专为旅行爱好者打造的在线平台。我们提供丰富多样的旅游......