5 分布式ID

news2025/1/10 15:38:24

这里讲一个比较常用的分布式防重复的ID生成策略,雪花算法

一个用户体量比较大的分布式系统必然伴随着分表分库,分机房部署,单体的部署方式肯定是承载不了这么大的体量。

雪花算法的结构说明

如下图所示:
在这里插入图片描述

雪花算法组成

从上图我们可以看出来雪花算法是64bit位的long类型的数值型的id。其中由4部分组成。

1bit为固定为0,表明生成的id为正数。

41bit位毫秒级的时间戳(可保留69年的时间戳)

标识位为10位,由5位的机器id和5位的服务id组成。5bit位能标识32个数值,32*32=1024 共能表示1024个数值,也就是说我们部署的服务可以水平扩展至1024个实例部署。在国内的应用中基本上是够用了。

12位的序列号位,表示同一个实例在1毫秒内能生成4096个序列,基本上也是够用了。

如果按照水平扩展应用还不能够承载我们的体量,我们可以对上面的组成进行改造。比如我们一个应用实例不会在1毫秒生成4096个数值,我们可以把时间戳的值改成35位,把6个bit均分到机器id和服务id上,也就是说我们可以部署256*256=65536个实例。

雪花算法的具体实现代码如下:

/**
 * 雪花算法工具类
 * 64位long类型的= 第一位0表示为正数 + 1到41的时间戳 + 5位的数据中心id + 5位的机器id + 12位的序列号
 * @author yusong
 * @20241111
 */
public class SnowflakeIdWorkerUtil {
      
      //开始时间戳 2025-01-01=1735660800000L
      private final long startTime = 1731400000000L;
      //数据中心所占雪花算法数据的位数
      private final long datacenterIdBits = 5L;
      //机器号所占雪花算法的位数
      private final long workerIdBits = 5L;
//    //支持最大的数据中心 结果为31
//    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
//    //支持最大的机器编号 结果为31
//    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
      //同一时间戳生成的序列号所占位数
      private final long sequenceBits = 12L;
      //机器号左移位数=序列号的位数 = 12
      private final long workerIdShift = sequenceBits;
      //数据中心左移位数= 序列号的位数 + 机器号的位数 = 17
      private final long datacenterIdShift = sequenceBits + workerIdBits;
      //时间戳左移位数= 序列号的位数 + 机器号的位数 + 数据中心位数 = 22
      private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
      //生成序列号的最大数=4095
      private final long sequenceMask = -1L ^ (-1L << sequenceBits);
      //数据中心编号
      private long datacenterId;
      //机器号
      private long workerId;
      //毫秒内序列从0开始
      private long sequence = 0L;
      //上次生成ID的时间截
      private long lastTimestamp = -1L;
      
      
      private static SnowflakeIdWorkerUtil sw = null;
      
      /**
       *
       * @param datacenterId 数据中心编号id
       * @param workerId           机器编号id
       * @return
       */
      public static synchronized SnowflakeIdWorkerUtil getSnowflakeId(long datacenterId,long workerId) {
            
            if(datacenterId>31||datacenterId<0) {
                  throw new RuntimeException("datacenterId必须是0到31位的整数");
            }
            if(workerId>31||workerId<0) {
                  throw new RuntimeException("workerId必须是0到31位的整数");
            }
            if(sw==null) {
                  sw = new SnowflakeIdWorkerUtil();
                  sw.datacenterId = datacenterId;
                  sw.workerId = workerId;
            }
            
            return sw;
      }
      
      /**
       * 私有的构造函数 防止外部new
       */
      private SnowflakeIdWorkerUtil() {}
      
      /**
       * 获取下一个id
       * @return
       */
      public synchronized long nextId() {
            
            long timestamp = timeGen();
            //如果当前时间戳小于上次的时间戳 说明系统时钟回退过 应该抛出异常
            if(timestamp<this.lastTimestamp) {
                  throw new RuntimeException("系统时钟回退异常,请检查系统设置");
            }
            //如果是同一时间戳生成id,则进行毫秒内序列
            if(this.lastTimestamp==timestamp) {
                  
                  sequence = (sequence + 1) & sequenceMask;
                  //同一毫秒内序列溢出 则等待下一秒
                  if(sequence==0) {
                        timestamp = nextMillis(lastTimestamp);
                  }
            }else {
                  sequence = 0L;
            }
            this.lastTimestamp = timestamp;
            
            return ((timestamp - startTime)<<timestampLeftShift)|(datacenterId<<datacenterIdShift)|(workerId<<workerIdShift)|sequence;
      }
      
      /**
       * 阻塞到下一毫秒,直到获取到新的时间戳
       * @param lastTimestamp
       * @return
       */
      private long nextMillis(long lastTimestamp) {
            
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
            return timestamp;
      }
      
      /**
       * 获取当前的时间戳
       * @return
       */
      private long timeGen() {
            return System.currentTimeMillis();
      }
      
      
      public static void main(String[] args) {
            
            
            for(int i=0;i<100;i++) {
                  SnowflakeIdWorkerUtil swu = SnowflakeIdWorkerUtil.getSnowflakeId(0, 0);
                  long id = swu.nextId();
                  System.out.println(Long.toBinaryString(id));
                  System.out.println(id);
            }
      }
      
}

雪花算法有个问题就是不能进行时钟回拨,如果进行时钟回拨就会生成重复的id。如果我们考虑以上问题可以在我们的程序中保留最近一次的生成时间,并拿来做校验,就可解决这个问题。办法总比问题多。

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

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

相关文章

Android wifi常见问题及分析

参考 Android Network/WiFi 那些事儿 前言 本文将讨论几个有意思的网络问题&#xff0c;同时介绍 Android 上常见WiFi 问题的分析思路。 网络基础Q & A 一. 网络分层缘由 分层想必大家很熟悉&#xff0c;是否想过为何需要这样分层&#xff1f; 网上大多都是介绍每一层…

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

音视频入门基础&#xff1a;MPEG2-PS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;1&#xff09;——MPEG2-PS官方文档下载 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ps文件 音视频入门基础…

读书笔记:分布式系统原理介绍

写在前面 已经大概三个月左右没有更新博客了&#xff0c;哈哈哈哈&#xff1b; 此博客是笔者在对《分布式系统原理介绍》进行概述&#xff0c;对于整个分布式系统协议的理解基于一些量化的指标考虑了数据的分布副本协议&#xff08;中心化/去中心化&#xff09;进行了总结&…

Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py

此脚本的主要功能是可视化点云数据文件&#xff08;.pcd 文件&#xff09;&#xff0c;并通过键盘交互选择演示数据的起始帧和结束帧&#xff0c;生成片段标记文件 (clip_marks.json) 主要流程包括&#xff1a; 用户指定数据目录&#xff1a;检查目录是否存在并处理标记文件 -…

MBM指尖六维力触觉传感器:高灵敏度、低漂移,精准掌控力学世界

MBM指尖六维力触觉传感器是一种专为机器人设计的高性能传感器。它通过集成三轴力和三轴力矩的感知能力&#xff0c;能够精准捕捉复杂的力学信息。传感器采用MEMS与应变体复合测量技术&#xff0c;具备数字输出功能&#xff0c;显著降低漂移并减少安装偏移的影响。其紧凑轻便的设…

C#,图论与图算法,任意一对节点之间最短距离的弗洛伊德·沃肖尔(Floyd Warshall)算法与源程序

一、弗洛伊德沃肖尔算法 Floyd-Warshall算法是图的最短路径算法。与Bellman-Ford算法或Dijkstra算法一样&#xff0c;它计算图中的最短路径。然而&#xff0c;Bellman Ford和Dijkstra都是单源最短路径算法。这意味着他们只计算来自单个源的最短路径。另一方面&#xff0c;Floy…

为答疑机器人扩展问题分类与路由功能

1.意图识别 2. 构建路由模块 简单的意图识别 from chatbot import llmfrom config.load_key import load_key load_key()prompt 【角色背景】 你是一个问题分类路由器&#xff0c;需要识别问题的类型。 --- 【任务要求】 问题的类型目前有&#xff1a;公司内部文档查询、内…

spring boot启动源码分析(三)之Environment准备

上一篇《spring-boot启动源码分析&#xff08;二&#xff09;之SpringApplicationRunListener》 环境介绍&#xff1a; spring boot版本&#xff1a;2.7.18 主要starter:spring-boot-starter-web 本篇开始讲启动过程中Environment环境准备&#xff0c;Environment是管理所有…

Pandas-RFM会员价值度模型

文章目录 一. 会员价值度模型介绍二. RFM计算与显示1. 背景2. 技术点3. 数据4. 代码① 导入模块② 读取数据③ 数据预处理Ⅰ. 数据清洗, 即: 删除缺失值, 去掉异常值.Ⅱ. 查看清洗后的数据Ⅲ. 把前四年的数据, 拼接到一起 ④ 计算RFM的原始值⑤ 确定RFM划分区间⑥ RFM计算过程⑦…

【理论】测试框架体系TDD、BDD、ATDD、DDT介绍

一、测试框架是什么 测试框架是一组用于创建和设计测试用例的指南或规则。框架由旨在帮助 QA 专业人员更有效地测试的实践和工具的组合组成。 这些指南可能包括编码标准、测试数据处理方法、对象存储库、存储测试结果的过程或有关如何访问外部资源的信息。 A testing framewo…

FreeU: Free Lunch in Diffusion U-Net 笔记

FreeU: Free Lunch in Diffusion U-Net 摘要 作者研究了 U-Net 架构对去噪过程的关键贡献&#xff0c;并发现其主干部分主要在去噪方面发挥作用&#xff0c;而其跳跃连接主要是向解码器模块引入高频特征&#xff0c;这使得网络忽略了主干部分的语义信息。基于这一发现&#…

JAVA 使用apache poi实现EXCEL文件的输出;apache poi实现标题行的第一个字符为红色;EXCEL设置某几个字符为别的颜色

设置输出文件的列宽&#xff0c;防止文件过于丑陋 Sheet sheet workbook.createSheet(FileConstants.ERROR_FILE_SHEET_NAME); sheet.setColumnWidth(0, 40 * 256); sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(2, 20 * 256); sheet.setColumnWidth(3, 20 * 25…

【STM32】无源蜂鸣器播放音乐《千与千寻》,HAL库

目录 一、工程链接 二、简单介绍 主要特点&#xff1a; 应用&#xff1a; 驱动电路&#xff1a; 三、原理图 四、cubeMX配置 时钟配置 五、keil配置 六、驱动编写 演奏函数 主函数编写 七、效果展示 八、驱动附录 music.h music.c 一、工程链接 STM32无源蜂鸣…

在 Vue 3 集成 e签宝电子合同签署功能

实现 Vue 3 e签宝电子合同签署功能&#xff0c;需要使用 e签宝提供的实际 SDK 或 API。 e签宝通常提供针对不同平台&#xff08;如 Web、Android、iOS&#xff09;的 SDK&#xff0c;而 Web 端一般通过 WebView 或直接使用嵌入式 iframe 来加载合同签署页面。 下面举个 &…

04、Redis深入数据结构

一、简单动态字符串SDS 无论是Redis中的key还是value&#xff0c;其基础数据类型都是字符串。如&#xff0c;Hash型value的field与value的类型&#xff0c;List型&#xff0c;Set型&#xff0c;ZSet型value的元素的类型等都是字符串。redis没有使用传统C中的字符串而是自定义了…

如何用Python编程实现自动整理XML发票文件

传统手工整理发票耗时费力且易出错&#xff0c;而 XML 格式发票因其结构化、标准化的特点&#xff0c;为实现发票的自动化整理与保存提供了可能。本文将详细探讨用python来编程实现对 XML 格式的发票进行自动整理。 一、XML 格式发票的特点 结构化数据&#xff1a;XML 格式发票…

Linux——修改USB网卡设备节点名称

修改驱动&#xff1a; 测试&#xff1a; 参考资料&#xff1a; https://blog.csdn.net/ablexu2018/article/details/144868950

(STM32笔记)十二、DMA的基础知识与用法 第三部分

我用的是正点的STM32F103来进行学习&#xff0c;板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话&#xff0c;用的也是这个板子和教程。 DMA的基础知识与用法 三、DMA程序验证1、DMA 存储器到存储器模式实验&#xff08;1&#xff09;DMA结构体解释&#xff08;2…

论文笔记(六十一)Implicit Behavioral Cloning

Implicit Behavioral Cloning 文章概括摘要1 引言2 背景&#xff1a;隐式模型的训练与推理3 隐式模型与显式模型的有趣属性4 policy学习成果5 理论见解&#xff1a;隐式模型的通用逼近性6 相关工作7 结论 文章概括 引用&#xff1a; inproceedings{florence2022implicit,titl…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯&#xff0c;&#xff08;德语&#xff1a;Johann Carl Friedrich Gau&#xff0c;英语&#xff1a;Gauss&#xff0c;拉丁语&#xff1a;Carolus Fridericus Gauss&#xff09;1777年4月30日–1855年2月23日&#xff0c;德国著名数学家、物理学家…