正在开发中的 PostgreSQL 18 将会引入两个与 UUIDv7 相关的函数,可以为数据库提供具有全局唯一性和时间排序性的随机 ID。
PostgreSQL 17 以及早期版本中,我们可以使用 gen_random_uuid() 函数生成一个完全随机的 UUIDv4:
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
UUID 随机性导致的问题在于作为主键数据时性能不如自增字段,UUIDv7 则解决了这个问题。
PostgreSQL 18 开始支持 uuidv7() 函数,用于生成 UUIDv7 数据:
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
同时,新版本还提供了一个 uuid_extract_timestamp() 函数,用于获取 UUID 数据中包含的时间戳:
SELECT uuid_extract_timestamp( '01941c04-4185-7efe-b171-12949bdf8bd8'::uuid );
uuid_extract_timestamp
----------------------------
2024-12-31 10:20:28.549+01
时间戳精度为毫秒。
另外,uuidv7() 函数还支持一个时间偏移参数:
SELECT uuid_extract_timestamp( uuidv7( '-21 years' ) );
uuid_extract_timestamp
----------------------------
2003-12-31 10:28:11.544+01
你可能会问,如果我们使用 PostgreSQL 17 以及更早版本,有没有办法生成 UUIDv7 呢?
当然可以,请参考下面的 SQL 语句:
select
-- timestamp
lpad(to_hex(((extract(epoch from now()) * 1000)::bigint >> 16)), 8, '0') || '-' ||
lpad(to_hex(((extract(epoch from now()) * 1000
+ (date_part('milliseconds', now())::bigint % 1000))::bigint & 0xffff)), 4, '0') || '-' ||
-- version
lpad(to_hex((0x7000 + (random() * 0x0fff)::int)), 4, '0') || '-' ||
-- variant
lpad(to_hex((0x8000 + (random() * 0x3fff)::int)), 4, '0') || '-' ||
-- randomness
lpad(to_hex((floor(random() * (2^48))::bigint >> 16)), 12, '0') AS uuid7;
uuid7 |
------------------------------------+
0194368b-51d2-7336-90a9-00001c61a9c3|
现在,你还会选择自增类型作为数据库主键吗?