PGSQL Phriday #015:主键:UUID、CUID 还是 TSID?

2 阅读4分钟

PGSQL Phriday #015:主键:UUID、CUID 还是 TSID?

摘要: 本文探讨了将 UUID、CUID 和 TSID 作为 PostgreSQL 数据库主键策略的争论。在审视了各种方案的特点后,作者的结论是:以上方案都不推荐——PostgreSQL 原生的 identity 列(identity column)配合适当的数据建模能提供最佳的性能和可靠性。

2024年2月3日 · 761 字 · 4 分钟阅读

原文链接


本月 PGSQLPhriday 活动的主题是 UUID,源自我发起的关于该话题的讨论。

我将回答一位开发者朋友的问题:"当我们需要主键时,用什么最好?UUID、CUID 还是 TSID?"

TL;DR:请不要使用

答案取决于场景!是的,这是顾问之间的经典笑料:你可以用"视情况而定"来回答客户的所有问题。原因很简单:如果答案是显而易见的,你就不需要顾问了!

定义

首先,让我们确保理解这里涉及的不同概念:

UUID:根据维基百科,"通用唯一标识符(UUID)是用于计算机系统中信息的 128 位标签。'全局唯一标识符(GUID)'一词也常被使用。按照标准方法生成,UUID 在实际应用中实际上是唯一的。它们的唯一性不依赖于中央注册机构或生成方之间的协调,与大多数其他编号方案不同。虽然 UUID 重复的概率不为零,但通常认为可以忽略不计。"

CUID:根据 cuid2 项目(cuid 项目已弃用),"现代 web 应用的需求与早期 GUID(全局唯一标识符)和 UUID(通用唯一标识符)时代的应用有所不同。特别是,CUID2 旨在提供比任何现有 GUID 或 UUID 实现更强的唯一性保证,并防止泄露被引用数据或生成该 ID 的系统的任何信息。"

TSID:根据 Vlad Mihalcea 的说法,"如果你计划将 UUID 值存储在主键列中,那么使用 TSID(时间排序唯一标识符)会更好。"

我的 PostgreSQL 专家观点

我是一名 PostgreSQL 专家。因此,我的观点只基于 PostgreSQL 能提供什么。PostgreSQL 原生不支持 CUID 或 TSID,但支持 UUID。我不赞成让应用程序创建主键,因为这不是正确的做法。数据库应该负责主键,应该由数据库生成主键。

我对主键的观点非常简单:如果关心性能,就不要折腾,好好建模数据,使用"generated always as identity column"(始终生成为身份列),并在自然主键上添加唯一约束。不要玩弄那些如果你不深入理解其含义就可能会反咬你一口的东西。(包括主键的)唯一性在 PostgreSQL 中通过 B-tree 索引强制实现。当索引的数据是顺序的时,这些索引非常有趣。使用 UUID 和 CUID 你会失去这个优势。你可以从我同事 Tomas 的文章中看到,UUID 生成的 WAL(Write-Ahead Logging,预写日志)比顺序 ID 高得多。当然,如果你的工作负载是随机更新而不是顺序插入,那么你就不会有使用顺序 ID 的 WAL 优势。不过,顺序 ID 更短更轻量。

UUID 用例

让我们来看看你可能首先想使用 UUID、CUID 或 TSID 的原因:

  1. 你想在应用程序中生成 ID,避免使用中央系统。使用 PostgreSQL 的话,这不是一个问题。PostgreSQL 就是一个中央系统,你无法绕过它。

  2. 该值是随机的,可以在 UI 中安全使用,而不会让用户猜测其他 ID 并访问他们不应该访问的数据。保护好你的访问权限!当用户猜测一个标识符并访问他们不应该访问的数据时,问题是标识符可以被猜测吗?问题是应用程序没有检查用户被允许做什么!

  3. 我找不到另一个原因了,如果你有,请给我指教。如果我能被证明是错的,我会非常高兴。

结论

所以,依我看来,在 PostgreSQL 中使用 UUID、CUID 或 TSID 没有任何正当理由。至于"当我们想要水平扩展时该怎么办?"的问题,由于 PostgreSQL 原生不支持水平扩展(除非你想用 Foreign Data Wrappers(外部数据包装器)和分区表自己构建整个系统),根据我的经验,在我见过的用例中,超过 90% 的情况下你不需要扩展 PostgreSQL,因为它本身就很优秀,只要你正确维护它,它应该能正常表现。

每个人都知道优化的第一条规则是"如果你不是专家,就不要做"(第二条是"如果你是专家,先不要做")。我要说,对关系型数据库来说,优化的第零条规则是"仔细建模你的数据"。


标签:PGSQL Phriday, PostgreSQL