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…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...