Java分布式ID

1 什么是分布式ID

        分布式ID是指在分布式系统中生成的唯一标识符,用于标识不同实体或数据的唯一性。在分布式系统中,多台机器并行处理任务,为了确保生成的ID在整个系统中的唯一性,需要采用特殊的算法来生成分布式ID。

        在传统的单机系统中,可以使用自增序列或随机数来生成唯一ID。但在分布式系统中,多台机器同时生成ID时可能会导致重复的情况。为了解决这个问题,需要引入一种分布式ID生成算法,确保在整个分布式系统中生成的ID是唯一的。

        分布式ID的设计要考虑并发性能、全局唯一性和扩展性等因素,并根据具体的系统需求选择合适的算法实现。常见的分布式ID生成算法包括雪花算法(Snowflake)、UUID(Universally Unique Identifier)等。

2 UUID

        UUID(Universally Unique Identifier)是一种标识符,用于在计算系统中生成全局唯一的ID。它是由128位的二进制数表示,通常以32位的十六进制字符串形式展示。

        UUID的生成算法保证了在全球范围内生成的ID具有极高的唯一性。它不依赖于中央控制器或集中式的ID生成服务,可以在分布式系统中生成唯一的标识符。

        生成的UUID通常呈现为以下形式:

        xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

        其中,"x"表示十六进制数字。具体的格式和含义取决于UUID的版本和变体。

        UUID具有以下特点:

  • 全局唯一性:UUID的生成算法确保在全球范围内生成的ID具有极高的唯一性,不同系统和节点生成的UUID几乎不会重复。
  • 无序性:UUID是基于随机数或名称空间生成的,没有严格的递增或递减顺序。
  • 可用性:UUID生成算法简单高效,生成过程不需要依赖网络通信或中央服务器。

        UUID广泛应用于分布式系统、数据库、标识符生成等场景,用于唯一标识实体、数据记录或资源。它在保证全局唯一性的同时,提供了一种简单可靠的标识方案。

        在Java中,UUID类是用于生成和操作UUID的工具类,提供了方便的方法来生成和操作UUID。它可以用于在Java应用程序中生成唯一的标识符,例如在分布式系统中跟踪实体或记录的唯一标识。

        UUID类位于java.util包下,并提供了以下主要方法:

  • randomUUID(): 静态方法,用于生成一个随机的UUID。该方法会使用随机数生成算法生成一个符合UUID标准的随机UUID。
  • toString(): 将UUID对象转换为字符串表示。

        使用java生产一个UUID的代码示意如下:

UUID uuid = UUID.randomUUID();
String uuidString = uuid.toString();

        UUID的优势与劣势

        优势:

  • 全局唯一性:UUID的生成算法保证了生成的ID在全球范围内的唯一性,不依赖于中央控制器或集中式的ID生成服务。这意味着在分布式系统中不同节点生成的UUID不会发生冲突。
  • 无序性:UUID是基于随机数生成的,没有严格的递增或递减的顺序。这对于某些应用场景来说是有利的,因为它不会暴露数据生成的时间或其他敏感信息。
  • 简单易用:UUID的生成算法相对简单,生成过程高效,可以快速地生成ID。
  • 无需网络通信:生成UUID不需要依赖网络通信或中央服务器,每个节点可以独立生成ID,减少了系统的复杂性和对网络的依赖。

        劣势:

  • 长度较长:UUID通常由32位的十六进制数表示,加上分隔符的话长度更长。这会增加存储和传输的成本,特别是在大规模的数据集合中使用UUID作为标识符时。
  • 不可读性:UUID是由数字和字母组成的字符串,对人类来说不太友好,不易于直观理解。这在调试、日志记录和数据查询等场景中可能会带来一些困难。
  • 无法排序:由于UUID是基于随机数生成的,它们之间没有严格的顺序关系。这导致在某些需要按照时间或顺序访问数据的场景中,UUID并不适合作为排序依据。
  • 不适合作为数据库索引:由于UUID的无序性和长度较长,将UUID作为数据库的主键或索引可能会导致性能下降,尤其是在大规模数据集合和频繁的索引操作中。

3 雪花算法

        雪花算法(Snowflake Algorithm)是一种常用的分布式ID生成算法,最初由Twitter开发并广泛应用于分布式系统中。它的设计目标是生成全局唯一且有序递增的ID,适用于大规模分布式系统中的标识符需求。相对于UUID,雪花算法ID的好处是长度短并且还是有序递增的。

        雪花算法生成的ID是一个64位的长整型数值,具体格式如下所示:

0 | 0000000000 | 00000 | 00000 | 000000000000

        其中:最高位是未使用的符号位(为0),接下来的41位表示时间戳,然后是5位的数据中心标识符,5位的机器标识符,最后是12位的序列号。

        雪花算法生成ID的过程如下:

  • 时间戳:使用当前时间戳减去一个固定的起始时间(如2010年1月1日),得到一个相对时间。这样可以确保在一定时间内生成的ID具有递增的趋势。
  • 数据中心标识符和机器标识符:每个数据中心分配一个唯一的数据中心标识符,每台机器分配一个唯一的机器标识符。这样可以在分布式环境中唯一标识每个数据中心和每台机器。
  • 序列号:在同一毫秒内生成的ID,通过序列号来进行区分,保证同一机器在同一毫秒内生成的ID的唯一性。序列号从0开始,每生成一个ID自增1,最多可以达到12位的长度(即4096个序列号)。

        雪花算法的优点包括:

  • 全局唯一性:在分布式系统中,不同节点生成的ID不会产生冲突,确保全局唯一性。
  • 有序性:生成的ID在时间上有序递增,方便按照时间排序和索引。
  • 高性能:生成ID的算法简单高效,不依赖于网络通信或中央服务器。

        然而,雪花算法也有一些限制:

  • 依赖于系统时间:由于使用时间戳作为ID的一部分,系统时间的回拨或不同节点之间的时间差异可能会导致生成的ID不唯一或不按照预期顺序递增。
  • 数据中心和机器标识符的分配需要管理:为每个数据中心和机器分配唯一标识符需要一定的管理工作,确保标识符的唯一性。
  • 时钟回拨问题:如果系统时间发生回拨(校准调整时间),可能会导致生成的ID不唯一或不按照预期递增。

        在使用雪花算法时,需要根据具体应用的需求和场景,合理设置数据。

/*** 雪花算法*/
public class SnowFlake {/** 开始时间截 (2020-01-01) */private final long twepoch = 1577808000000L;/** 机器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;private static SnowFlake idWorker;static {idWorker = new SnowFlake(getWorkId(),getDataCenterId());}//==============================Constructors=====================================/*** 构造函数* @param workerId 工作ID (0~31)* @param dataCenterId 数据中心ID (0~31)*/public SnowFlake(long workerId, long dataCenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));}if (dataCenterId > maxDataCenterId || dataCenterId < 0) {throw new IllegalArgumentException(String.format("dataCenterId 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();}private static Long getWorkId(){try {String hostAddress = Inet4Address.getLocalHost().getHostAddress();int[] ints = StringUtils.toCodePoints(hostAddress);int sums = 0;for(int b : ints){sums += b;}return (long)(sums % 32);} catch (UnknownHostException e) {// 如果获取失败,则使用随机数备用return RandomUtils.nextLong(0,31);}}private static Long getDataCenterId(){int[] ints = StringUtils.toCodePoints(StringUtils.isEmpty(SystemUtils.getHostName())?"defaultvalue":SystemUtils.getHostName());int sums = 0;for (int i: ints) {sums += i;}return (long)(sums % 32);}/*** 静态工具类** @return*/public static Long generateId(){long id = idWorker.nextId();return id;}//==================Test===================/** 测试 */public static void main(String[] args) {System.out.println(System.currentTimeMillis());long startTime = System.nanoTime();for (int i = 0; i < 50000; i++) {long id = SnowFlake.generateId();System.out.println(id);}System.out.println((System.nanoTime()-startTime)/1000000+"ms");}}

        使用上述的雪花算法实现,调用 SnowFlake.generateId() 方法来生成唯一的ID。确保为每个实例分配唯一的数据中心ID和机器ID。

        这样,你就能够在童小码项目中生成分布式有序的商品、课程等业务关键字的ID,同时保持高性能和全局唯一性。

4 总结

        1、分布式ID是指在分布式系统中生成的唯一标识符,用于标识不同实体或数据的唯一性

  • 在分布式系统中,多台机器并行处理任务,为了确保生成的ID在整个系统中的唯一性,需要采用特殊的算法来生成分布式ID
  • 常用方式有雪花算法、UUID等

        2、UUID(Universally Unique Identifier)是一种标识符,用于在计算系统中生成全局唯一的ID

  • 由128位的二进制数表示,通常以32位的十六进制字符串形式展示
  • 长度较长、不可读性、无法排序和不适合作为数据库索引等劣势需要在具体应用中进行权衡和考虑

        3、雪花算法(Snowflake Algorithm)是一种常用的分布式ID生成算法

  • 生成全局唯一且有序递增的ID,适用于大规模分布式系统中的标识符需求
  • 相对于UUID,雪花算法ID的好处是长度短并且还是有序递增的

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

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

相关文章

Docker的数据管理、网络通信和dockerfile

目录 一、Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 2. 数据卷容器 2.1 创建数据卷容器 2.2 使用--volume-from来挂载test1 二、端口映射 三、容器互联 1. 创建容器互联 ​编辑2. 进入test2测试&#xff08;ping 容器名/别名&#xff09; 四、Dock…

Python的pytest框架(5)--测试标记(Markers)

该篇将循序渐进地详细拆解 pytest.mark 装饰器&#xff1a; 目录 一、概念 二、标记的基本结构与使用 三、标记在测试中的层次应用 四、标记的筛选与运行 五、标记与测试行为控制 六、标记与测试参数化 七、标记的注册与自定义 1、通过pytest.ini配置文件&#xff1a;…

SpringBoot钩子函数

在Java Spring Boot中&#xff0c;并没有直接称为“钩子函数”的概念&#xff0c;但你可以通过实现特定的接口、注解、事件监听或使用AOP&#xff08;面向切面编程&#xff09;来实现类似的功能。这些功能允许你在应用的特定点插入自定义逻辑&#xff0c;类似于钩子函数的作用。…

c++11详解

目录 1.列表初始化 2.声明 3.右值引用和移动语句 4. c11新的类功能 5. 可变参数模板 6.lambda表达式 7.包装器 8. 后言 1. 列表初始化 1.1 {}的初始化 (1) c98标准规定可以使用{}对数组以及结构体进行统一的列表初始化. struct Point {int _x;int _y; };int main() {in…

Python数据权限的管理通常涉及到几个关键组件:身份验证,、授权和访问控制。这通常是通过使用数据库、ORM(对象关系映射)框架、API框架和中间件

在Python中&#xff0c;数据权限的管理通常涉及到几个关键组件&#xff1a;身份验证&#xff0c;、授权和访问控制。这通常是通过使用数据库、ORM&#xff08;对象关系映射&#xff09;框架、API框架和中间件等技术来实现的。以下是一些建议的步骤和工具&#xff0c;用于在Pyth…

C语言面经

25.类型相同的两个指针之间不能进行的运算 指针主要用于存储变量的内存地址。对于同类型的指针变量之间&#xff0c;有一些规则&#xff1a; a. 小于运算&#xff08;<&#xff09;&#xff1a;指针间的小于比较是基于它们指向的内存地址。地址较小的指针在小于比较中被认为…

【Unity】shader中参数传递

1、前言 unity shader这个对于我来说是真的有点难&#xff0c;今天这篇文章主要还是总结下最近学习到的一些东西&#xff0c;避免过段时间忘记了&#xff0c;可能有不对&#xff0c;欢迎留言纠正。 2、参数传递的两种方式 2.1 语义传递 语义传递这个相对来说是简单的 shad…

Csharp_pta2_2

7-7 C# 1.12 区间找数 编写控制台应用程序&#xff0c;根据用户输入的a、b、c、d值&#xff08;均为正整数且a不大于b&#xff09;&#xff0c;输出在[a, b]区间中能被c整除&#xff0c;但是不能被d整除的数。 输入格式: 用户在一行中输入四个正整数&#xff0c;分别对应a、…

数组模拟几种基本的数据结构

文章目录 数组模拟单链表数组模拟双链表数组实现栈数组模拟队列总结 数组模拟单链表 首先类比结构体存储单链表&#xff0c;我们需要一个存放下一个节点下标的数组&#xff0c;还需要一个存储当前节点的值的数组&#xff0c;其次就是一个int类型的索引&#xff0c;这个索引指向…

Python 实现视频去抖动技术

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 视频去抖动是视频处理中的一项重要技术&#xff0c;它可以有效地减少视频中由于相机震动或手…

springSecurity简单直接说明

引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombo…

MyBatis处理SQL中的特殊字符

方式一&#xff1a;转义字符 如下案例&#xff1a; < 表示小于的转义字符 <!-- 在Mapper XML文件中定义SQL语句 --> <select id"selectById" resultMap"BaseResultMap">select *from userwhere id < #{id}; </select>方式二&am…

设计模式:依赖倒转原则(Dependency Inversion Principle,DIP)介绍

依赖倒转原则&#xff08;Dependency Inversion Principle&#xff0c;DIP&#xff09;是面向对象设计原则之一&#xff0c;它强调高层模块不应该依赖于底层模块&#xff0c;二者都应该依赖于抽象。同时&#xff0c;抽象不应该依赖于具体实现细节&#xff0c;具体实现细节应该依…

嵌入式开发学习--进程、线程

什么是进程 进程和程序的区别 概念 程序&#xff1a;编译好的可执行文件&#xff0c;存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09;&#xff0c;程序是静态的&#xff0c;没有任何执行的概念。 进程&#xff1a;一个独立的可调度的任务&#xff0c;执行一…

高可靠性部署系列(3)--- ASG双机热备(HA)

高可靠性部署系列(3)--- ASG双机热备(HA) 前言网络拓扑设备选型网络规划组网需求配置思路操作步骤步骤 1 HA接口管理地址配置步骤 2 HA全局配置步骤 3 配置同步步骤 4 接口状态同步组创建结果验证前言 近期有读者留言:“因华为数通模拟器仅能支持USG6000V的防火墙,无法支…

东方博宜1009 - 数组逆序

题目描述 给你 nn 个整数&#xff0c;将其逆序输出。 输入 第一行一个整数 nn &#xff08;3 \le n \le 1003≤n≤100)代表数的个数。 第二行 nn 个整数&#xff08;空格隔开&#xff09;&#xff08;这些数在 0 \sim 10^60∼106 之间)。 输出 nn 个整数&#xff08;空格…

恶补《操作系统》3_1——王道学习笔记

3内存管理 3.1_1 内存的基础知识 1、什么是内存&#xff0c;作用 &#xff08;1&#xff09;内存&#xff1a;内存用来存放数据。程序执行前需要先放到内存中才能被CPU处理――缓和CPU与硬盘之间的速度矛盾。 &#xff08;2&#xff09;内存存储单元&#xff1a;每个地址对应…

AIGC技术的发展现状和未来趋势

AIGC&#xff08;人工智能生成内容&#xff09;技术是指利用人工智能算法自动生成文本、图像、音频、视频等各类内容的技术。随着深度学习等技术的快速发展&#xff0c;AIGC技术在最近几年取得了显著进步&#xff0c;并在多个领域展现出巨大的潜力。 ​ 编辑 发展现状&#x…

前端数字计算精度问题

计算精度问题通常发生在浮点数运算中&#xff0c;由于浮点数的表示所限&#xff0c;可能导致精度损失。 举例 // 比如 0.1 0.2 // 结果为 0.30000000000000004 0.3 - 0.1 // 结果为 0.19999999999999996vue vue 使用decimal.js 解决小数相加合计精确度丢失问题 微信小程序 …

【2024系统架构设计】回顾历史,查缺补漏篇 ①

前言 hello,大家好: 💡💡💡 我们一起来备考软考高级系统架构设计师吧,本专栏提供综合知识、案例科目、论文(论点和部分示例范文)等内容,包括知识点总结和记忆小妙招哦。 🚀🚀🚀 可以减少资料查找和收集的时间,提高效率,我们一起