PostgreSQL 18 预览: UUID v7 生成函数

John Doe 三月 26, 2025

你在设计表结构的时候,习惯使用 UUID 值作为主键吗?现在,PostgreSQL 提供了内置的顺序 UUID 生成函数。

在山坡上奔跑的大象

特性提交日志

添加 UUID 版本 7 的生成函数。

本次提交引入了一个 SQL 函数uuidv7(),该函数会按照 RFC 9652 规范生成 UUID 版本 7 的值。UUIDv7 结合了以毫秒为单位的 Unix 时间戳和随机位,兼具唯一性和可排序性。

在该特性的实现中,在毫秒级时间戳之后紧跟着存储了 12 位的亚毫秒部分,这是在 RFC 规范中称为 “rand_a” 的空间。这确保了在一毫秒内具有额外的单调性。rand_a 位还起到了计数器的作用。之所以选择亚毫秒时间戳,是为了在同一后端生成的 UUID 能单调递增,即使系统时钟倒退或在极高频率下生成 UUID 时,也能如此。因此,在同一后端生成的 UUID 的单调性得到了保证。

本次提交还扩展了uuid_extract_timestamp()函数,以支持 UUID 版本 7。

此外,为现有的 SQL 函数gen_random_uuid()添加了别名uuidv4(),以保持一致性。

讨论:https://postgr.es/m/CAAhFRxitJv%3DyoGnXUgeLB_O%2BM7J2BJAmb5jqAT9gZ3bij3uLDA%40mail.gmail.com

示例

很多人使用 UUID 时的一个困扰是,它们不可排序。当然,在某些情况下这可能有其合理性,但一般来说,我们还是希望能在某种程度上判断,给定两个 UUID,哪个生成得更早,哪个更晚,至少能大致判断。

幸运的是,自 2022 年以来,实际上有一种 UUID 格式 / 版本使这成为可能,即 UUID 版本 7。

首先,让我们看看它是什么样的。在 PostgreSQL 18 之前,我们可以使用gen_random_uuid()函数获取一个 UUID(版本 4),它是完全随机的:

SELECT gen_random_uuid() FROM generate_series(1,10);
           gen_random_uuid            
--------------------------------------
 ba604c90-b70a-4e34-990c-19f6279a7078
 a9196baa-2326-4fa2-9e57-5232ae1fee04
 ee7076f8-0177-4e36-a7eb-ae7d56fef3e2
 1cd2e852-7642-4936-9b45-4a4f821a00ff
 feadb02a-2746-4e8e-83a1-31d9c3203049
 1a72dc31-c95e-4ce7-b848-ca29a9c614ea
 f57ac276-d22b-4d32-8f38-79614a126a7e
 f92f98dd-e359-490f-9008-bdf38c86cbdc
 39c51f13-9035-43a8-b6aa-c54de897ba08
 1c8c0929-65f9-4b9d-abab-63117a028d0b
(10 rows)

在上面的输出中,第三段的值开头都是 4,这是一个版本标记。

现在,在 PostgreSQL 18 中,我们可以生成 UUID v7 的值,如下所示,显然有些与时间相关的部分变化不大:

SELECT uuidv7() FROM generate_series(1,10);
                uuidv7
--------------------------------------
 01941c04-4185-7ea3-ab00-82c8a75adf41
 01941c04-4185-7efe-b171-12949bdf8bd8
 01941c04-4185-7f05-89b4-0409545aefc2
 01941c04-4185-7f09-9952-b4b515d8b0c8
 01941c04-4185-7f0d-a009-252decd3c9ea
 01941c04-4185-7f10-89d1-651c6b36fdbb
 01941c04-4185-7f14-a77e-edccd6f145bf
 01941c04-4185-7f18-b109-c02d263b7457
 01941c04-4185-7f1b-8185-24c7c304b7e2
 01941c04-4185-7f1f-a950-dec8a092fbe5
(10 rows)

有了这些值,你现在还可以使用uuid_extract_timestamp()函数,来获取 UUID 的创建时间戳:

SELECT uuid_extract_timestamp('01941c04-4185-7efe-b171-12949bdf8bd8'::uuid);
   uuid_extract_timestamp   
----------------------------
 2024-12-31 17:20:28.549+08
(1 row)

根据规范,时间戳的精度为毫秒。这对于绝大多数场景来说应该足够了。

还有一个有趣的功能,它允许生成带有一定时间偏移的 UUID。

例如,如果我对新生成的 UUID v7 运行uuid_extract_timestamp,得到的结果会大致与now()相同:

SELECT uuid_extract_timestamp(uuidv7()), now();
   uuid_extract_timestamp   |              now
----------------------------+-------------------------------
 2024-12-31 17:27:41.122+08 | 2024-12-31 17:27:41.122049+08
(1 row)

但是,我也可以这样做:

SELECT uuid_extract_timestamp(uuidv7('-21 years'));
   uuid_extract_timestamp
----------------------------
 2003-12-31 17:28:11.544+08
(1 row)

看起来确实很有用。感谢社区的所有相关人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/78c5e141e9c139fc2ff36a220334e4aa25e1b0eb

了解更多

PostgreSQL 中的顺序 UUID 生成器