在Postgres中生成非碎片化UUID?

6
如果我理解正确,完全随机的UUID值会创建分散的索引。更准确地说,缺乏共同前缀会导致索引无法进行密集的trie存储。
有人建议使用uuid_generate_v1()或uuid_generate_v1mc()代替uuid_generate_v4()来避免这个问题。
然而,似乎UUID规范的第一个版本将ID的低位放在前面,防止共享前缀。此外,这个时间戳是60位,可能有些过度。
相比之下,一些数据库提供了非标准的UUID生成器,其中包含前导32位时间戳和12字节的随机数。例如,Datomic的Squuid [1] [2]
在Postgres中使用这样的“Squuids”是否有意义?如果有,如何在pgplsql中高效地生成这样的ID?

随着您插入或更新更多数据,索引可能会变得分散,这意味着如果您使用普通索引,则B+树将变得不太平衡。当然,您可以重新索引以使树更加平衡。从您的问题中,我认为您想要看到哪个UUID版本可以使树更加平衡。我认为您应该使用pgbench运行一些基准测试,以查看性能成本是否有差异,以及计划是否生成良好。如果任何解决方案都适用于您的应用程序,那么其余部分纯粹是学术研究。 - andreim
防止索引中的密集 trie 存储:为什么要假设 trie 存储?通常,您会使用 B 树索引来处理 UUID。只有在通过 SP-GiST 类型的索引的 text_ops 操作符族请求时,才会获得 trie 存储。 - Daniel Vérité
我在这里回答了一个类似的问题。基本上,我建议使用ULIDs。 - DharmaTurtle
1个回答

3
请注意,插入连续的索引条目只有在您不删除值且所有更新都生成堆只元组时,才会导致更密集的索引 heap only tuples
如果您想要顺序唯一的索引值,为什么不自己构建呢?
您可以使用微秒级别的clock_timestamp()作为bigint,并附加来自循环序列的值:
CREATE SEQUENCE seq MINVALUE 0 MAXVALUE 999 CYCLE;

SELECT CAST(
          floor(
             EXTRACT(epoch FROM t)
          ) AS bigint
       ) % 1000000 * 1000000000
     + CAST(
          to_char(t, 'US') AS bigint
       ) * 1000
     + nextval('seq')
FROM (SELECT clock_timestamp()) clock(t);

为了避免混淆他人-- clock_timestamp() 返回一个带有时区的微秒级别的时间戳。在pgsql中无法获得纳秒级别的准确度;这段代码只是将微秒值乘以1000。 - Molomby
谢谢,我会修复描述。 - Laurenz Albe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接