【问题标题】:Generate non-fragmenting UUIDs in Postgres?在 Postgres 中生成非分段 UUID?
【发布时间】:2017-06-20 22:57:58
【问题描述】:

如果我理解正确,完全随机的 UUID 值会创建碎片索引。或者,更准确地说,缺少公共前缀会阻止索引中的密集 trie 存储。

我看到有人建议使用 uuid_generate_v1() 或 uuid_generate_v1mc() 而不是 uuid_generate_v4() 来避免这个问题。

但是,UUID 规范的第 1 版似乎首先具有 ID 的 位,从而防止共享前缀。另外,这个时间戳是 60 位的,这似乎有点过头了。

相比之下,一些数据库提供的非标准 UUID 生成器具有前导 32 位的时间戳,然后是 12 个随机字节。请参阅 Datomic 的 Squuid,例如 12

在 Postgres 中使用这样的“Squids”实际上有意义吗?如果是这样,如何使用 pgplsql 有效地生成此类 ID?

【问题讨论】:

  • 当您插入或更新更多数据时,您可能会得到索引碎片,这意味着如果您使用的是普通索引,您的 B+Tree 会变得不那么平衡。当然,您可以重新索引以使树更加平衡。根据您的问题,我假设您想查看哪个 UUID 版本使树更平衡。我认为您应该使用pgbench 运行一些基准测试,以查看性能成本是否存在差异以及计划是否生成良好。如果任何解决方案适用于您的应用,那么剩下的就是纯粹的学术研究。
  • 防止索引中的密集 trie 存储:为什么假设 trie 存储?通常,您会为 UUID 使用 B 树索引。只有通过 SP-GiST 索引类型的 text_ops 运算符系列,您才能获得 trie 存储。
  • 我回答了这样的问题here。基本上我建议ULIDs

标签: postgresql indexing uuid


【解决方案1】:

请注意,仅当您不删除值并且所有更新都会生成 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() 返回一个精确到微秒的timestamp with time zone。在 pgsql 中不可能达到纳秒级的精度;此代码只是将微秒值乘以 1000。
  • 谢谢,我会修正描述的。
猜你喜欢
  • 2012-09-12
  • 2019-02-03
  • 2020-05-22
  • 2021-12-13
  • 2016-10-23
  • 2017-06-25
  • 2013-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多