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

简介: 1.使用自增主键的弊端首先在实际工程中我们很少用1,2,3......这样的自增主键,原因如下:主键冲突性能问题安全问题主键冲突:比如我要跨数据库进行数据同步、或者在分布式系统中跨“分区”进行数据同步,不难想象,1,2,3......这种递增的单数字是极容易产生冲突的。

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)共同制定:


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


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


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


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


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);
}

首先从Holder静态内部类中获取一个SecureRandom对象。Holder类是一个懒加载的单例类,用于确保SecureRandom对象只被创建一次,并且线程安全。


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


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


将第7个字节的高4位清零,再将其低4位设置为4,表示这是UUID版本4。

将第9个字节的高2位清零,再将其第7位设置为1,表示这是符合IETF标准的UUID。

最后,将修改后的字节数组作为参数,调用UUID(byte[] data)构造方法生成一个UUID对象。

2.2.雪花算法

2.2.1.概述

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

06287e2bd36f406880186f6e6246576f.png

雪花算法的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;
    // 其他字段省略...
}


目录
相关文章
|
4月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
289 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
4月前
|
数据采集 前端开发 算法
基于朴素贝叶斯算法的新闻类型预测,django框架开发,前端bootstrap,有爬虫有数据库
本文介绍了一个基于Django框架和朴素贝叶斯算法开发的新闻类型预测系统,该系统具备用户登录注册、后台管理、数据展示、新闻分类分布分析、新闻数量排名和新闻标题预测等功能,旨在提高新闻处理效率和个性化推荐服务。
|
4月前
|
安全 数据管理 关系型数据库
深入理解数据库主键
【8月更文挑战第31天】
100 0
|
5月前
|
算法 数据库
|
5月前
|
文字识别 算法 Java
文本,保存图片09,一个可以用id作为图片名字的pom插件,利用雪花算法生成唯一的id
文本,保存图片09,一个可以用id作为图片名字的pom插件,利用雪花算法生成唯一的id
|
6月前
|
算法 数据中心 Python
基于python雪花算法工具类Snowflake-来自chatGPT
基于python雪花算法工具类Snowflake-来自chatGPT
130 4
|
6月前
|
算法 Java
基于java雪花算法工具类SnowflakeIdUtils-来自chatGPT
基于java雪花算法工具类SnowflakeIdUtils-来自chatGPT
321 3
|
6月前
|
算法 PHP 数据中心
基于php雪花算法工具类Snowflake -来自chatGPT
基于php雪花算法工具类Snowflake -来自chatGPT
115 2
|
6月前
|
算法 数据中心 C++
基于C++雪花算法工具类Snowflake -来自chatGPT
基于C++雪花算法工具类Snowflake -来自chatGPT
|
5月前
|
存储 算法 Java
分布式自增ID算法---雪花算法(SnowFlake)Java实现
分布式自增ID算法---雪花算法(SnowFlake)Java实现
297 0