数据库如何合理生成主键:UUID、雪花算法

news2024/11/26 5:39:09

目录

1.使用自增主键的弊端

2.主键生成算法

2.1.UUID

2.1.1.概述

2.1.2.JAVA中的UUID

2.2.雪花算法

2.2.1.概述

2.2.2.JAVA中使用雪花算法


1.使用自增主键的弊端

首先在实际工程中我们很少用1,2,3......这样的自增主键,原因如下:

  • 主键冲突
  • 性能问题
  • 安全问题

主键冲突:

比如我要跨数据库进行数据同步、或者在分布式系统中跨“分区”进行数据同步,不难想象,1,2,3......这种递增的单数字是极容易产生冲突的。

性能问题:

自增主键在数据库中使用的是自增序列来生成主键值,而在高并发情况下,多个线程可能同时请求获取下一个自增值,此时会发生竞争,因为数据库需要保证每个自增值只会被分配一次。为了保证自增值的唯一性,数据库使用锁机制来避免多个线程同时获取同一个自增值。

安全问题:

使用自增主键可以很容易地猜测出下一条记录的主键值,这可能会导致一些安全问题。

综上所述,我们可以发现使用复杂主键是很有必要的,要生成复杂且具有唯一性的主键就需要依赖主键生成算法了。

2.主键生成算法

常见的主键生成算法:

  • UUID
  • 雪花算法

2.1.UUID

2.1.1.概述

UUID,通用唯一标识符,UUID算法的核心思想是生成一个128位的唯一标识符,通常表示为32个十六进制数字的字符串。每个UUID会尽量保证其唯一性。

UUID算法目前有几个官方推出的版本,由Internet工程任务组(IETF)、国际标准化组织(ISO)和国际电信联盟(ITU)共同制定:

  1. UUIDv1:基于时间戳和MAC地址生成UUID,保证同一台计算机上生成的UUID是唯一的,但不适合在分布式系统中使用。

  2. UUIDv2:基于DCE安全机制的UUID,使用POSIX的UID/GID和当前时间生成UUID,不常用。

  3. UUIDv3:基于命名空间和字符串生成UUID,使用MD5散列算法生成UUID。

  4. UUIDv4:使用随机数生成UUID,保证在所有计算机上都是唯一的。

  5. UUIDv5:与UUIDv3类似,但使用SHA-1散列算法生成UUID。

其实本质上来说UUID只是个概念,UUID的算法我们甚至可以自己去写一个,只是我们自己写的肯定会没那么优质,保证唯一性的效果不会好。

2.1.2.JAVA中的UUID

工程上我们一般都是在JAVA后端生成UUID作为每条要插入数据库的数据的主键,JAVA中生成UUID很简单:

import java.util.UUID;

public class UUIDExample {
    public static void main(String[] args) {
        // 生成一个随机的UUID
        UUID uuid = UUID.randomUUID();
        System.out.println("Random UUID: " + uuid.toString());
    }
}

生成过程其实也很简单:

public static UUID randomUUID() {
    SecureRandom ng = Holder.numberGenerator;

    byte[] randomBytes = new byte[16];
    ng.nextBytes(randomBytes);
    randomBytes[6]  &= 0x0f;  /* clear version        */
    randomBytes[6]  |= 0x40;  /* set to version 4     */
    randomBytes[8]  &= 0x3f;  /* clear variant        */
    randomBytes[8]  |= 0x80;  /* set to IETF variant  */
    return new UUID(randomBytes);
}
  1. 首先从Holder静态内部类中获取一个SecureRandom对象。Holder类是一个懒加载的单例类,用于确保SecureRandom对象只被创建一次,并且线程安全。

  2. 然后生成一个长度为16的字节数组randomBytes,使用ng.nextBytes(randomBytes)方法填充随机数。

  3. 接下来将字节数组randomBytes中的一些特定位进行修改,以符合UUID版本4的格式要求:

    • 将第7个字节的高4位清零,再将其低4位设置为4,表示这是UUID版本4。
    • 将第9个字节的高2位清零,再将其第7位设置为1,表示这是符合IETF标准的UUID。
  4. 最后,将修改后的字节数组作为参数,调用UUID(byte[] data)构造方法生成一个UUID对象。

2.2.雪花算法

2.2.1.概述

雪花算法(Snowflake Algorithm)是Twitter公司开发的一种分布式ID生成算法,用来生成64位的唯一ID。它的核心思想是:利用一个64位的long型数字作为全局唯一ID,高位部分表示时间戳,中间部分表示机器ID,低位部分表示在此机器上的序列号。

雪花算法的64位long型数字由以下几个部分组成:

  • 符号位(1 bit):由于long型数字是有符号的,而雪花算法生成的ID必须是正数,所以符号位固定为0。
  • 时间戳部分(41 bit):记录时间戳,精确到毫秒级别,可以使用当前时间减去一个固定的起始时间,得到一个相对时间戳,从而可以使用41位二进制数表示该时间戳可支持的时间范围为约69年。
  • 机器ID部分(10 bit):可以配置多台机器,每台机器都分配一个唯一的ID,用于在分布式环境下防止ID重复。
  • 序列号部分(12 bit):表示在同一毫秒内生成的序列号,可以使用自增来实现,最多支持4096个ID。

2.2.2.JAVA中使用雪花算法

原生JDK中并没有提供雪花算法的实现,在一些常用的ORM框架中支持了使用雪花算法生成主键ID的功能,如hibernate、mybatis-plus。

以使用Hibernate为例:

可以在实体类的主键字段上使用@GenericGenerator注解和@GeneratedValue注解,其中@GenericGenerator注解用来指定主键生成器的名称和类型,@GeneratedValue注解则用来指定主键生成策略和生成器的名称,例如:

@Entity
@Table(name = "user")
public class User {
 
    @Id
    @GenericGenerator(name = "snowflake", strategy = "com.xxx.snowflake.SnowflakeIdGenerator")
    @GeneratedValue(generator = "snowflake")
    @Column(name = "id")
    private Long id;
 
    // ...
}

以mybatis-plus为例:

配置主键生成器为雪花算法的生成器

mybatis-plus:
  global-config:
    db-config:
      id-type: ASSIGN_ID
      key-generator: com.baomidou.mybatisplus.core.incrementer.SnowflakeKeyGenerator

然后给主件指定使用所配置的主键生成器

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

@Data
public class User {

    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    private String name;

    private Integer age;

    // 其他字段省略...
}

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

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

相关文章

MRI 成像方法和脉冲序列整理

以下内容为MRI期末复习笔记,仅供复习参考使用。 成像方法 2D重建 反投影重建 傅里叶重建 运用中心截面定理,一个三维物体的二维投影的傅里叶变换精确地等于物体的傅里叶变换的中心截面 上述定理换成二维同理 重建步骤 1、取得投影 P ( r , ϕ ) P(r,…

一招解决ChatGPT对话经常中断问题:KeepChatGPT插件

大家好,我是可夫小子,关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加:keeepdance,备注:chatgpt,拉你进群。 在访问ChatGPT官网进行聊天时,经常遇到了『与服务器断开』Something wet …

C++语法(22)---- 哈希表的闭散列和开散列

C语法(21)---- 模拟map和set_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130354019?spm1001.2014.3001.5501 目录 1.哈希表的介绍 1.stl中的哈希表使用 2.比较 3.哈希的原理 4.哈希映射的方法 1.直接定址法 2.…

FreeRTOS 软件定时器

文章目录 一、软件定时器简介二、定时器服务/Daemon 任务三、单次定时器和周期定时器四、复位软件定时器1. 函数 xTimerReset()2. 函数 xTimerResetFromISR() 五、创建软件定时器1. 函数 xTiemrCreate()2. 函数 xTimerCreateStatic() 六、开启软件定时器1. 函数 xTimerStart()2…

条件构造器Wrapper

本文来说下mybatis-plus中的条件构造器Wrapper 文章目录 条件构造器程序实例 条件构造器 十分重要&#xff1a;Wrapper 我们写一些复杂的sql就可以使用它来替代&#xff01; 程序实例 测试一 Overridepublic List<OrderInfo> getOrderInfo() {// id等于2的数据QueryWrapp…

K210单片机的按键检测

这个图片是程序的效果&#xff0c;按下按键后蓝灯亮起&#xff0c;松开按键后蓝灯熄灭。 主要用的的就是函数的构造方法和使用方法&#xff1a; GPIO(ID,MODE,PULL,VALUE) GPIO 对象。 【ID】内部 GPIO 编号; 【MODE】GPIO 模式&#xff1b; GPIO.IN &#x…

保龄球游戏的获胜者、找出叠涂元素----2023/4/30

保龄球游戏的获胜者----2023/4/30 给你两个下标从 0 开始的整数数组 player1 和 player2 &#xff0c;分别表示玩家 1 和玩家 2 击中的瓶数。 保龄球比赛由 n 轮组成&#xff0c;每轮的瓶数恰好为 10 。 假设玩家在第 i 轮中击中 xi 个瓶子。玩家第 i 轮的价值为&#xff1a; …

SpringMVC学习总结(三)SpringMVC获取请求参数的几种方法解决获取请求参数的乱码问题CharacterEncodingFilter过滤器

SpringMVC学习总结&#xff08;三&#xff09;SpringMVC获取请求参数的几种方法/解决获取请求参数的乱码问题/CharacterEncodingFilter过滤器 一、通过ServletAPI获取请求参数 将HttpServletRequest作为控制器方法的形参&#xff0c;此时HttpServletRequest类型的参数表示封装…

Elasticsearch --- 数据聚合、自动补全

一、数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这…

山东专升本计算机第十章-计算思维

计算思维 分支主题 3 10.2计算思维与算法 考点 3 算法概论 算法就是对计算思维解决问题的方法的描述是计算机求解的核心和关键。 基本特征 • 有穷性 • 算法必须能执行有线各步骤之后停止 • 确定性 • 算法的每一个步骤都必须有明确的定义不应该在理解时产生二义性 • 可…

【软考数据库】第六章 数据库技术基础

目录 6.1 基本概念 6.1.1 关于数据的基本概念 6.1.2 数据库管理系统的功能 6.1.3 数据各个发展阶段的特点 6.1.4 数据库系统的体系结构 6.2 数据模型 6.2.1 三级模式两级映像 6.2.2 数据模型_模型分类 6.2.3 数据模型_组成…

Linux man 命令详解

man 命令 Linux man 命令用于显示 Linux 操作系统中的手册页&#xff08;manual page&#xff09;&#xff0c;它提供了对 Linux 操作系统中各种命令、函数、库等的详细说明&#xff0c;man 命令有许多参数。 参数介绍 下面简要介绍一下主要参数的功能&#xff1a; -f&…

Java面试题总结 | Java面试题总结7- Redis模块(持续更新)

Redis 文章目录 Redisredis的线程模型Redis的Mysql的区别Redis和传统的关系型数据库有什么不同&#xff1f;Redis常见的数据结构zset数据结构Redis中rehash过程redis为什么不考虑线程安全的问题呢Redis单线程为什么还能这么快&#xff1f;为什么Redis是单线程的&#xff1f;red…

第十五章 角色移动旋转实例

本章节我们创建一个“RoleDemoProject”工程&#xff0c;然后导入我们之前创建地形章节中的“TerrainDemo.unitypackage”资源包&#xff0c;这个场景很大&#xff0c;大家需要调整场景视角才能看清。 接下来&#xff0c;我们添加一个人物模型&#xff0c;操作方式就是将模型文…

《CTFshow-Web入门》07. Web 61~70

Web 61~70 web61~65题解 web66知识点题解 web67知识点题解 web68知识点题解 web69知识点题解 web70知识点题解 ctf - web入门 web61~65 题解 这几个题都和 web58 一样。可能内部禁用的函数不一样吧。但 payload 都差不多。不多解释了。 以下解法随便挑一个即可。可能不同题会…

使用Quartz.net + Topshelf完成服务调用

概述&#xff1a; Quartz.NET 是一个开源作业调度库&#xff0c;可用于在 .NET 应用程序中调度和管理作业。它提供了一个灵活而强大的框架&#xff0c;用于调度作业在特定的日期和时间或以固定的时间间隔运行&#xff0c;并且还支持复杂的调度场景&#xff0c;例如 cron 表达式…

39.Java-interface接口

interface接口 1.interface2.接口的定义和使用3.接口中成员的特点4. 接口和类之间的关系5. 实例6. 接口中新增的方法6.1 JDK8以后新增2种方法6.1.1 允许在接口中定义默认方法6.1.2 允许在接口中定义静态方法 6.2 JDK9以后新增的方法6.3 小结 7. 接口总结 1.interface 接口就是…

Netty内存管理--内存池PoolArena

一、写在前面 到这里, 想必你已知道了Netty中的内存规格化(SizedClass), Page和SubPage级别的内存分配, 但是具体使用者不应该关心应该申请page还是subpage。而且从过去的经验来说, 申请page/subpage的数量也是个动态值, 如果申请使用完之后就释放那使用内存池的意义就不大。N…

Linux 之十九 编译工具链、.MAP 文件、.LST 文件

.map 文件和 .lst 文件是嵌入式开发中最有用的俩调试辅助文件。现在主要从事 RISC-V 架构&#xff0c;开始与 GCC 打交道&#xff0c;今天就重点学习一下 GCC 的 .map 文件、.lst 文件&#xff0c;并辅助以 ARMCC 和 IAR 作为对比。 编译工具链 .map 文件和 .lst 文件都是由编…

【数据结构】第十三站:排序性质

文章目录 一、文件外与文件内排序二、非比较排序之计数排序1.绝对映射2.相对映射3.代码实现 三、排序的稳定性 一、文件外与文件内排序 如下图所示是我们常见的的排序算法&#xff0c;也是我们已经使用代码实现过的 上面这七种排序算法我们都可以称之为文件内排序。但是归并排…