SpringBoot生成唯一ID的方式
1.为什么要生成唯一ID?
数据唯一性:每个记录都需要有一个独一无二的标识符来确保数据的唯一性。这可以避免重复的数据行,并有助于准确地查询、更新或删除特定的记录。
数据完整性:通过使用唯一ID,可以保证数据库中的数据完整性。例如,在关系数据库中,外键通常引用主表的唯一ID,以建立和维护表之间的关联。
高效查询:唯一ID通常是索引的一部分,这意味着使用它们进行查询可以非常快速和高效。这对于提高应用程序性能特别重要,尤其是在处理大量数据时。
简化逻辑处理:拥有一个唯一的标识符可以简化很多业务逻辑的处理。例如,当需要引用或共享特定数据项时,唯一ID提供了一个简单而直接的方法。
分布式系统支持:在分布式系统或微服务架构中,唯一ID(特别是全局唯一ID)是必不可少的。它们允许不同的服务能够独立生成不冲突的ID,从而简化了跨服务的数据整合和通信问题。
易于扩展和维护:随着系统的发展和需求的变化,唯一ID使得添加新功能或者修改现有功能变得更加容易。例如,重构数据库结构时,唯一ID可以帮助更平滑地迁移数据。
2.生成唯一ID的基本要求
唯一性:这是最基本的要求,即生成的ID在整个系统中必须是独一无二的。特别是在分布式系统中,需要确保不同节点生成的ID也不会发生冲突。
全局唯一性(如果适用):在某些场景下,如分布式系统或微服务架构中,可能需要ID不仅在同一数据库或系统内唯一,而且在所有相关的系统和数据库中也保持唯一。
不可预测性:为了安全考虑,尤其是在涉及用户敏感信息或重要业务逻辑时,ID应该是难以预测的。这可以防止恶意猜测或其他安全问题。
顺序性(如果需要):有些应用可能需要ID有一定的顺序性,例如按时间顺序排列,以便于排序、分页等操作。但需要注意的是,严格的顺序性可能会对系统的扩展性和性能造成影响。
高效性:生成ID的过程应该尽可能快速且消耗资源少,以避免成为系统性能瓶颈。
长度适中:ID的长度应该适中,既能保证足够的空间来确保唯一性,又不至于过长导致存储和传输效率降低。
兼容性:生成的ID应与现有的系统和协议兼容,不会因为格式或编码问题而引起错误。
可扩展性:随着系统的发展和规模的增长,生成ID的方法应能方便地进行扩展,以适应更高的需求。
3.常见生成方法
- UUID(通用唯一识别码)
基于时间的UUID(Version 1):结合时间戳和MAC地址生成。
基于随机数的UUID(Version 4):使用随机数生成。
基于命名空间的UUID(Version 3 和 5):基于命名空间和名称的哈希值生成。
优点:
全局唯一性:UUID的设计保证了在全球范围内的唯一性,适用于分布式系统。
无需中央协调:无需依赖数据库或其他中央服务生成ID,减少了系统复杂性。
生成速度快:基于随机数的UUID生成速度非常快,适用于高并发场景。缺点:
长度较长:UUID长度为128位,通常表示为36个字符(包括连字符),这在存储和传输时占用较多空间。例如,在数据库中存储UUID会比存储整数类型占用更多的空间。
无序性:UUID通常是随机生成的,缺乏时间上的顺序性。这在数据库索引中可能导致性能问题,因为插入操作可能需要在B树索引中频繁分裂节点。
可读性差:UUID对人类不友好,难以记忆和识别,不利于在用户界面或日志中直接使用。
缺乏业务相关性:UUID不包含任何业务信息,如时间戳或区域信息,难以用于业务分析和追踪。
- 适用场景
- 分布式系统:在多节点、多数据中心的环境中,UUID是生成唯一标识符的理想选择。
- 无需排序的场景:如果不需要按时间或其他顺序对ID进行排序,UUID是一个不错的选择。
- 高并发环境:UUID的生成速度非常快,适用于需要快速生成唯一标识符的高并发场景。
数据库自增ID是一种常见的生成唯一标识符的方法,通过在数据库表中设置自增字段(如
AUTO_INCREMENT),每次插入新记录时,数据库会自动为该字段生成一个唯一的、递增的整数。
优点:
简单易用:实现简单,只需在数据库表中设置自增字段,无需额外的代码或配置。
有序性:自增ID是有序的,有利于数据库索引的性能,特别是在使用B树索引时。
节省存储空间:整数类型的ID通常比UUID占用更少的存储空间。
可读性较好:整数ID对人类相对友好,易于识别和记忆。
缺点:
单点瓶颈:在分布式数据库环境中,自增ID难以保证全局唯一性,通常需要依赖单个数据库实例来生成ID,这会成为系统的单点瓶颈,影响性能和可扩展性。
难以水平扩展:如果系统需要水平扩展到多个数据库实例,自增ID的生成会成为问题,因为每个实例都会生成自己的ID序列,导致ID冲突。
依赖数据库:生成ID依赖于数据库,这在数据库故障或高负载时可能成为问题。
缺乏业务相关性:自增ID不包含任何业务信息,如时间戳或区域信息,难以用于业务分析和追踪。
- 适用场景
- 单体应用:在单体应用中,数据库自增ID是一个简单且有效的方法。
- 无需分布式唯一性的场景:如果系统不需要在多个数据库实例或多个数据中心中保持全局唯一性,自增ID是一个不错的选择。
- 对ID有序性有要求的场景:如果需要按时间或其他顺序对ID进行排序,自增ID的有序性是一个优势。
4.雪花算法的实现过程如下
获取当前时间戳,精确到毫秒级别。
根据给定的数据中心ID和机器ID,生成一个10位的二进制数。
将时间戳左移22位,将数据中心ID左移17位,将机器ID左移12位,然后使用位或操作符将它们组合成一个64位的二进制数。
如果在同一毫秒内生成了多个ID,使用序列号来区分它们,序列号从0开始递增,最多可以生成4096个序列号。
优点:全局唯一性:在分布式系统中,雪花算法可以确保生成的ID全局唯一。
有序性:生成的ID按照时间戳有序递增,便于数据管理和查询。
高并发:每毫秒可以生成4096个ID,适合高并发场景。
缺点:依赖服务器时间:如果服务器时间回拨,可能会导致生成重复的ID。可以通过记录最后一个生成ID的时间戳来解决这个问题。
序列号浪费:在分库分表时,如果序列号一直从0开始,可能会导致数据倾斜和不均匀分布。
`适用场景:分布式系统:如分布式数据库、分布式锁等,需要全局唯一且有序的ID。
高并发场景:如订单号生成、用户ID生成等
- 实现代码
public class SnowflakeIdGenerator {// 开始时间戳 (2023-01-01)private final long twepoch = 1672502400000L;// 机器ID所占的位数private final long workerIdBits = 5L;// 数据标识ID所占的位数private final long datacenterIdBits = 5L;// 支持的最大机器ID,结果是31 (这个值在位运算中不会溢出)private final long maxWorkerId = -1L ^ (-1L << workerIdBits);// 支持的最大数据标识ID,结果是31private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 序列号在ID中所占的位数private final long sequenceBits = 12L;// 机器ID需要左移的位数,12private final long workerIdShift = sequenceBits;// 数据标识ID需要左移的位数,17private final long datacenterIdShift = sequenceBits + workerIdBits;// 时间戳需要左移的位数,22private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;// 生成序列的掩码,这里为4095 (0b111111111111=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;public SnowflakeIdGenerator(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 = datacenterId;}/*** 产生下一个ID*/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;}protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}
}
相关文章:
SpringBoot生成唯一ID的方式
1.为什么要生成唯一ID? 数据唯一性:每个记录都需要有一个独一无二的标识符来确保数据的唯一性。这可以避免重复的数据行,并有助于准确地查询、更新或删除特定的记录。 数据完整性:通过使用唯一ID,可以保证数据库中的数…...
通俗易懂的分类算法之K近邻详解
通俗易懂的分类算法之K近邻详解 用最通俗的语言和例子,来彻底理解 K近邻(K-Nearest Neighbors,简称 KNN) 这个分类算法。不用担心复杂的数学公式,我会用生活中的例子来解释,保证你一听就懂! 1.…...
CSDN markdown 操作指令等
CSDN markdown 操作指令等 页内跳转 [内容](#1) <div id"1"> </div>...
【linux】文件与目录命令 - uniq
文章目录 1. 基本用法2. 常用参数3. 用法举例4. 注意事项 uniq 命令用于过滤文本文件中相邻的重复行,并支持统计重复次数或仅保留唯一行。它通常与 sort 命令配合使用,因为 uniq 只识别相邻的重复行。 1. 基本用法 语法: uniq [选项] [输入…...
零信任沙箱:为网络安全筑牢“隔离墙”
在数字化浪潮汹涌澎湃的今天,网络安全如同一艘船在波涛汹涌的大海中航行,面临着重重挑战。数据泄露、恶意软件攻击、网络钓鱼等安全威胁层出不穷,让企业和个人用户防不胜防。而零信任沙箱,就像是一座坚固的“隔离墙”,…...
【金融量化】Ptrade中交易环境支持的业务类型
1. 普通股票买卖 • 特点: 普通股票买卖是最基础的交易形式,投资者通过买入和卖出上市公司的股票来获取收益。 ◦ 流动性高:股票市场交易活跃,买卖方便。 ◦ 收益来源多样:包括股价上涨的资本利得和公司分红。 ◦ 风险…...
【Java---数据结构】链表 LinkedList
1. 链表的概念 链表用于存储一系列元素,由一系列节点组成,每个节点包含两部分:数据域和指针域。 数据域:用于存储数据元素 指针域:用于指向下一个节点的地址,通过指针将各个节点连接在一起,形…...
紧跟 Web3 热潮,RuleOS 如何成为行业新宠?
Web3 热潮正以汹涌之势席卷全球。从金融领域的创新应用到供应链管理的变革,从社交媒体的去中心化尝试到游戏产业的全新玩法探索,Web3 凭借其去中心化、安全性和用户赋权等特性,为各个行业带来了前所未有的机遇。在这股热潮中,Rule…...
CC++的内存管理
目录 1、C/C内存划分 C语言的动态内存管理 malloc calloc realloc free C的动态内存管理 new和delete operator new函数和operator delete函数 new和delete的原理 new T[N]原理 delete[]的原理 1、C/C内存划分 1、栈:存有非静态局部变量、函数参数、返回…...
Spark核心之02:RDD、算子分类、常用算子
spark内存计算框架 一、目标 深入理解RDD弹性分布式数据集底层原理掌握RDD弹性分布式数据集的常用算子操作 二、要点 ⭐️1. RDD是什么 RDD(Resilient Distributed Dataset)叫做**弹性分布式数据集,是Spark中最基本的数据抽象,…...
【Resis实战分析】Redis问题导致页面timeout知识点分析
事故现象:前端页面返回timeout 事故回溯总结一句话: (1)因为大KEY调用量,随着白天自然流量趋势增长而增长,最终在业务高峰最高点期占满带宽使用100%。   (2&#x…...
单一职责原则(设计模式)
目录 问题: 定义: 解决: 方式 1:使用策略模式 示例:用户管理 方式 2:使用装饰者模式 示例:用户操作 方式 3:使用责任链模式 示例:用户操作链 总结 推荐 问题&a…...
生理信号概念
rPPG 信号(远程光电容积脉搏波信号) 原理: 基于光电容积脉搏波描记法,利用普通摄像头,在一定距离外捕捉人体皮肤表面因心脏泵血导致的血液容积变化引起的细微颜色变化,通过图像处理和信号分析算法提取心率…...
安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露
安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露 - Wesley’s Blog 今天用着安卓 14 的板子的时候突然系统卡死。 查看日志发现launcher都被干掉了 03-04 06:13:35.544 7872 8479 I ActivityManager: vis BFGS 18740: com.android.launcher3 (pid 8407) se…...
android13打基础: 控件checkbox
测试checkbox的activity // todo: 高级控件checkbox public class Ch4_CheckBoxActivity extends AppCompatActivityimplements CompoundButton.OnCheckedChangeListener {Overrideprotected void onCreate(Nullable Bundle savedInstanceState) {super.onCreate(savedInstance…...
AI应用测试:遇到类ChatGPT的流式接口要如何压测?
先说结论: 使用最普遍的JMeter 就能支持类 OpenAI 的流式接口(如 ChatGPT 的流式聊天接口)的测试 总体设置 JMeter 支持测试 OpenAI 的流式接口,但需要额外配置(如启用 KeepAlive 和调整超时)。如果需要实时处理流式响应,使用 Regular Expression Extractor 或自定义脚…...
React面试葵花宝典之二
36.Fiber的更新机制 React Fiber 更新机制详解 React Fiber 是 React 16 引入的核心架构重构,旨在解决可中断渲染和优先级调度问题,提升复杂应用的流畅性。其核心思想是将渲染过程拆分为可控制的工作单元,实现更细粒度的任务管理。以下是其…...
在日常生活、工作中deepseek能帮我们解决哪些问题
在日常生活、工作中deepseek能帮我们解决哪些问题 DeepSeek极大降低了普通人使用AI的门槛,让AI快速渗透到人们的工作和生活中,无论是专业场景提效、教育学术赋能、商业创新甚至日常生活,都变得更加轻松。 当然这篇文章也参考了deepseek的回…...
【Java】IO流
Java IO流是Java中处理输入输出的核心机制,通过不同的流类型实现了对数据的高效读写。 一、IO流的分类 1. 按数据方向 输入流(Input Stream):从数据源(如文件、网络等)读取数据。输出流(Outp…...
HTML第三节
一.初识CSS 1.CSS定义 A.内部样式表 B.外部样式表 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title&g…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
