创建基于时间的 UUID

概述

在本文中,我们将会 对 UUIDs 和基于时间的 UUIDs(time-based UUIDs) 进行一些探讨。

当我们在对基于时间的 UUIDs 进行选择的时候,总会遇到一些好的方面和不好的方面,如何进行选择,也是我们将要简要探讨的内容。

同时我们还会对可能会使用类库进行一些比较和探索,以便于我们更好的做出选择。

UUIDs 和 基于时间的 UUIDs

UUID 的全称是 Universally Unique Identifier,中文为通用唯一识别码。

当生成 UUID 的时候,系统总会自动生成一个 128 位的 UUID。基于 UUID 的生产算法的不同,我们会有不同的版本。

UUID 的主要目的就是用来在全世界中唯一标识一个数据,而且需要保证生成的 UUID 在全世界范围内是不重复的。因此我们可以用来标识一个上下文,包括数据库系统,计算机系统中的消息,分布式系统中的对象等等。

为了实现这个目标,我们需要确保哪怕是在同一个时间瞬间生成的 UUID 也是不相同,这样能够让我们更好的利用 UUID 在分布式计算机系统中标识存在的对象。

基于时间的 UUID,通过字面就可以了解到,这个 UUID 是基于时间的,实际上这个 UUID 存在 UUID 设计中的第一版。

这个版本是基于随机数的,使用的基数为每 100 纳秒为一个单位,时间的起点为1582年10月15日。同时还需要加上当前计算机的网卡物理地址(MAC)。

在后续的版本中,UUID (v6 和 v7)也是基于时间的 UUID 生成算法,可以说是基于 UUID v1 的更新版本。

UUID v1 因为是基于时间的,所以具有排序功能,这个在对数据库的设计上就很有帮助,当我们使用 UUID v1 来作为 PK(主键)的时候,我们就知道了,我们创建的这条记录的时间戳是什么时候,这个对我们在对数据进行调试和问题分析的时候就很有帮助了。

有优势就自然会有劣势,因为我们是基于时间创建 UUID 的,那么在同一个系统产生 UUID 冲突的可能性就会大很多,假设在同一个时间点,我们创建了很多个 UUID,那么大概率就会有出现冲突,重复出现的情况。

在本文的后部分,我们会对这个可能出现的情况进行一些探索。

另外一个原因,就是在 UUID v1 版本中使用主机地址这种做法会潜在的增加系统的安全性问题。这就是 UUID v6 尝试希望解决的问题。

对比程序

为了对可能出现的 UUID 冲突进行演示。我们尝试使用程序来对比可能出现 UUID 冲突的可能性。

这个程序,将会创建 128 个线程,在每个线程中将会生成 100,000 个 UUID。

首先我们对需要使用的变量来进行一些初始化:

    int threadCount = 128;
    int iterationCount = 100_000;
    Map<UUID, Long> uuidMap = new ConcurrentHashMap<>();
    AtomicLong collisionCount = new AtomicLong();
    long startNanos = System.nanoTime();
    CountDownLatch endLatch = new CountDownLatch(threadCount);

如上面的程序所表示的内容,我们定义了 128 个线程,在这 128 个线程中,我们会循环 100,000 次。

同时,我们还初始化了一个 ConcurentHashMap 把我们生成的 UUID 存储到 ConcurentHashMap 中。

同时,我们还会记录出现 UUID 冲突的次数。

为了记录程序的性能,我们对程序开始时间和程序的结束也都进行了存储。在最后我们定义了一个 latch 等待所有线程的执行完成。

当定义完成后变量后,我们就需要启动线程并对线程序进行执行。

    for (long i = 0; i < threadCount; i++) {
        long threadId = i;
        new Thread(() -> {
            for (long j = 0; j < iterationCount; j++) {
                UUID uuid = Generators.timeBasedGenerator().generate();
                Long existingUUID = uuidMap.put(uuid, (threadId * iterationCount) + j);
                if(existingUUID != null) {
                    collisionCount.incrementAndGet();
                }
            }
            endLatch.countDown();
        }).start();
    }

在 UUID 的创建过程中,我们使用了 fasterxml 包中的 Generators,这个 Generators 使用的是 java.util.UUID 类来创建的。

在创建 UUID v1 的使用,使用 fasterxml 是我们常用的做法。

当 UUID 创建后,我们就把创建好的 UUID 存储到 Map 中,UUID 为 map 的 Key,当我们的 UUID 重复出现冲突的时候,Map 将会提示错误,我们程序就会捕获这个错误,然后把出现错误的计数器 + 1。

endLatch.await();
System.out.println(threadCount * iterationCount + " UUIDs generated, " + collisionCount + " collisions in "
        + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos) + "ms");

在程序的最后,CountDownLatch 的 await() 方法会等待所有的线程完成。

当所有线程完成后,我们就会把结果打印在计算机屏幕上。

下面就让我们开始对程序进行运行。

12800000 UUIDs generated, 0 collisions in 16913ms

上面出现了程序的运行结果,我们可以看到并没有出现 UUID 冲突的问题。

uuid-creator

Java UUID Creator 小巧并且使用比较广泛的 UUID 生成器。

使用这个生成器能够为你生成各种 UUID,但从使用的情况上来看并不如 java-uuid-generator 使用得更加频繁。

依赖

如希望在项目中使用这个生成器,需要把这个生成器添加到依赖中。

com.github.f4b6a3 uuid-creator 5.3.7 Copy

使用

这个库提供了 3 个基于时间生成的 UUID 方法。

  • UuidCreator.getTimeBased() – 遵守 RFC-4122 规范生成基于时间的 UUID
  • UuidCreator.getTimeOrdered() – 使用 gregorian epoch 处理的新 UUID 格式
  • UuidCreator.getTimeOrderedEpoch() – 使用 Unix epoch proposed 处理的新 UUID 格式

当我们导入包后,可以直接使用下面的方法来进行使用。

System.out.println("UUID Version 1: " + UuidCreator.getTimeBased());
System.out.println("UUID Version 6: " + UuidCreator.getTimeOrdered());
System.out.println("UUID Version 7: " + UuidCreator.getTimeOrderedEpoch());

我们可以看到生成 UUID 为我们通常认识的 UUID 格式。

UUID Version 1: 0da151ed-c82d-11ed-a2f6-6748247d7506
UUID Version 6: 1edc82d0-da0e-654b-9a98-79d770c05a84
UUID Version 7: 01870603-f211-7b9a-a7ea-4a98f5320ff8

冲突校验

我们还可以使用上面提供的生成代码放我我们开始的一部分使用的冲突校验程序中运行。

运行的结果为:

12800000 UUIDs generated, 0 collisions in 2595ms

通过这个库生成的基于时间的 UUID 我们也没有发现有冲突的情况。

Java UUID Generator (JUG)

依赖

Java UUID Generator (JUG) 在很多项目中因为要生成 UUID 而被使用,其中包括了有 生成UUID 的方法和格式化输出等一些内容。

生成的 UUID 是按照 (RFC-4122) 标准来生成的。

使用下面的代码来添加依赖,当前使用的最新版为 5.0.0

<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>5.0.0</version>
</dependency>

使用

这个库也同时提供了 3 个方法来生成基于时间的 UUID(包括 v1,v6 和 v7)。

我们可以使用下面的方法来进行命令的调用:

System.out.println("UUID Version 1: " + Generators.timeBasedGenerator().generate());
System.out.println("UUID Version 6: " + Generators.timeBasedReorderedGenerator().generate());
System.out.println("UUID Version 7: " + Generators.timeBasedEpochGenerator().generate());

当程序完成运行后,可以从控制台中看到输出:

UUID Version 1: e6e3422c-c82d-11ed-8761-3ff799965458
UUID Version 6: 1edc82de-6e34-622d-8761-dffbc0ff00e8
UUID Version 7: 01870609-81e5-793b-9e4f-011ee370187b

校验

将新的生成程序替换掉我们原有的代码的时候,我们可以对生成进行输出校验。

12800000 UUIDs generated, 0 collisions in 15795ms

从上面的输出来看,针对代码我们并没有发现有 UUID 冲突的问题。

但是针对运行结果,生成 UUID 的时间是不同的,这是因为 JUG 的生成速度通常要慢一点,对于我们 UUID 来说如果不大量一次性生成 UUID,这个通常不会是太大的问题。

在项目中,通常需要选择项目已有的依赖,可能在现有的项目中,使用 java-uuid-generator 库的情况要相对多一点。

结论

在本文中,我们对需要生成基于时间的 UUID 进行了一些探讨。同时基于时间的不同,UUID 有不同的版本。

JDK 自己并没有提供快速的基于时间的 UUID 生成方法。

JDK 中的 UUID.randomUUID() 方法生成的是 UUIDv4 的方法。

基于时间的 UUID 生成,我们通常会依赖使用第三方的类库。

创建基于时间的 UUID - Java - iSharkFly概述在本文中,我们将会 对 UUIDs 和基于时间的 UUIDs(time-based UUIDs) 进行一些探讨。 当我们在对基于时间的 UUIDs 进行选择的时候,总会遇到一些好的方面和不好的方面,如何进行选择,也是我们将要简要探讨的内容。 同时我们还会对可能会使用类库进行一些比较和探索,以便于我们更好的做出选择。 UUIDs 和 基于时间的 UUIDsUUID 的全称是 Universally Unique Identi…icon-default.png?t=N7T8https://www.isharkfly.com/t/uuid/15686

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: MySQL 中可以使用 UUID 来作为表的主键。UUID 是一种全局唯一标识符,使用它作为主键可以避免因为主键重复而导致的数据冲突。MySQL 中可以使用 `uuid()` 函数来生成 UUID。例如,可以使用以下语句创建一个使用 UUID 作为主键的表: ``` CREATE TABLE my_table ( id VARCHAR(36) NOT NULL PRIMARY KEY DEFAULT uuid(), name VARCHAR(20) NOT NULL ); ``` 在插入数据时,不需要指定主键的值,MySQL 会自动为主键生成一个 UUID。例如: ``` INSERT INTO my_table (name) VALUES ('Alice'); ``` 这将在 `my_table` 表中插入一行数据,其中 `id` 列的值为一个新生成的 UUID。 ### 回答2: MySQL的UUID是一个全局唯一标识符,它是一个128位的字符串,通常以36个字符串表示,其中包含了32位的16进制数字和连字符。UUID可以在多个系统之间进行唯一标识和交换数据。 MySQL的UUID主要有两种生成方式:第一种是使用UUID()函数生成,它是MySQL的内置函数,可以直接调用,每次调用都会生成一个新的UUID值。第二种是使用UUID_SHORT()函数生成,它是使用服务器状态和网络地址生成UUID的方法,相比UUID()函数生成的UUID长度更短,只有64位,但仍然是全局唯一的。 使用UUID作为主键有一些好处。首先,UUID可以在分布式系统中提供唯一性标识,避免了多个系统中生成相同的主键的问题。其次,UUID是随机生成的,可以避免主键的顺序递增导致的性能问题,如插入数据时的磁盘碎片等。此外,UUID可以在生成时就包含时间信息,方便分析和排序。 然而,使用UUID也存在一些问题。首先,UUID是随机生成的,不方便人眼阅读和调试,也不易于直接使用。其次,UUID作为主键,会增加索引的大小和查询的复杂性,对性能有一定的影响。另外,由于UUID的长度较长,会占用更多的存储空间和网络带宽。 总的来说,MySQL的UUID是一种全局唯一标识符,可以在分布式系统中提供唯一性标识。但在使用时需要权衡好唯一性、性能和存储等因素,选择适合的主键策略。 ### 回答3: MySQL UUID是一种全局唯一标识符(Universally Unique Identifier),它是通过128位数字来表示的。在MySQL中,UUID可以作为主键或其他唯一性字段的值。它具有以下特点: 1.全球唯一:UUID是根据计算机的MAC地址、时间戳和随机数等信息生成的,因此在全球范围内是唯一的。 2.长度固定:UUID的长度为32位十六进制数字,相对较短,不同于自增长的整数类型。 3.不依赖于数据库服务器:UUID是在应用程序级别生成的,不依赖于数据库服务器,因此可以在多个数据库服务器之间实现唯一性。 4.随机性:UUID的生成是基于随机数的,因此具有较高的随机性,这也保证了唯一性。 5.适用性广泛:UUID可以用于各种应用场景,如分布式系统中的数据同步、数据迁移和唯一标识等。 6.效率较低:由于UUID是一个较长的字符串,它在索引和查询时需要更多的存储空间和处理时间。 在使用MySQL UUID时,需要注意以下几点: 1.正确生成UUID:可以使用MySQL提供的UUID()函数来生成UUID值,也可以在应用程序中使用UUID库生成。 2.选择存储类型:UUID可以使用CHAR(36)或BINARY(16)两种存储类型,具体选择取决于需求,CHAR类型可以方便人类阅读和检索,而BINARY类型效率更高。 3.索引优化:由于UUID的长度较长,为了提高查询效率,可以考虑使用前缀索引、哈希索引或直接将UUID的一部分作为索引字段。 总之,MySQL UUID是一种全球唯一标识符,具有广泛的应用场景,但在使用时需要考虑存储空间和查询效率等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HoneyMoose

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值