GUID / UUID数据库键的优缺点

275

我过去曾经在许多数据库系统上工作过,如果所有数据库键都是GUID / UUID值,那么在数据库之间移动条目将变得更加容易。我已经考虑过几次这样做,但总会有一些不确定性,特别是在性能和无法通过电话读出的URL方面。

有没有人在数据库中广泛使用GUID?采用这种方式会带来什么优势,可能存在哪些缺陷?


3
Jeff在他的博客《主键:IDs vs GUIDs》中发表了一篇文章,链接为http://blog.codinghorror.com/primary-keys-ids-versus-guids/。 - jfs
1
可以为远程客户端使用 Hi-Lo 算法:https://dev59.com/ZXVC5IYBdhLWcg3weBA- - Neil McGuigan
Jeff Atwood的有关“[主键:ID vs GUID](https://blog.codinghorror.com/primary-keys-ids-versus-guids/)”的帖子已更新位置。感谢@jfs的参考。 - Adam Katz
@jfs 链接已更改为http://blog.codinghorror.com/primary-keys-ids-versus-guids/。 - cr0ss
10个回答

290

优点:

  • 可以离线生成。
  • 使复制变得微不足道(相对于 int 而言,int 会让它变得非常困难)。
  • ORM 通常喜欢使用它们。
  • 跨应用程序是唯一的。所以我们可以在应用程序中使用来自 CMS(guid)的主键,在我们的应用程序(也是 guid)中使用,并知道我们永远不会发生冲突。

缺点:

  • 占用更大的空间,但是空间更便宜。
  • 无法按 ID 排序以获得插入顺序。
  • 在 URL 中看起来很丑陋,但实际上,你为什么要将真正的 DB 键放在 URL 中!?(评论中有争议)
  • 手动调试更困难,但并不是太难。

个人而言,在任何规模较大的系统中,我都会将它们用于大多数主键,但我被一个到处复制的系统“训练”了,所以我们必须拥有它们。你的情况可能会有所不同。

我认为重复数据的问题是荒谬的 - 无论你如何做,都可能会出现重复数据。在我工作的任何地方,使用代理键通常是不被赞同的。但我们确实使用类似 WordPress 的系统:

  • 行的唯一 ID(GUID/其他)。对用户永远不可见。
  • 公共 ID 仅从某些字段(例如标题 - 将其设置为文章的标题)生成一次。

更新: 这篇文章得到了很多 +1,我认为我应该指出 GUID 主键的一个重大缺点:聚集索引。

如果你有很多记录,并且在 GUID 上有一个聚集索引,你的插入性能会非常差,因为你会将插入的记录随机放在项目列表中的不同位置(这就是关键),而不是放在末尾(这很快)。

因此,如果你需要插入性能,也许可以使用自动增量 INT,并在需要与他人共享时生成 GUID(例如,在 URL 中向用户显示它)。


213
你为什么要把真实的数据库密钥放在URL中?我不确定为什么这会让你困扰。还有其他可以使用的方法吗?看看Stack Overflow,它的URL中到处都是IDENTITY值,而且它完全正常。在URL中使用DB密钥并不妨碍你强制执行安全措施。 - Euro Micelli
22
不,它并不会影响,但是像 SEO 这样的东西通常会更好,如果其中没有关键词——特别是 GUID 这么长的内容。当然,这很容易被解决,所以我猜那可能有点过于笼统了。 - Nic Wise
7
好的回答。如果您能增加有关使用GUID的性能劣势的信息,例如,通过GUID进行连接、排序和索引都会比使用整数更慢,那就太好了。 GUID非常出色,但它们会带来成本,在性能至关重要时可能会带来麻烦。 - Doctor Jones
28
记住一件事情,人们经常更改页面、问题或论坛标题。对于SEO来说,在URL中添加一个小ID是有好处的,这样如果标题更改,你仍然知道从旧URL转发用户的位置。例如,example.com/35/old-and-busted变成了example.com/35/new-hotness,你的应用程序可以检查标题并使用301重定向用户。 - Xeoncross
9
使用GUID作为主键会使索引变得昂贵和缓慢,因此它们不是很好的主键选择。 - Matthew James Davis
显示剩余6条评论

21

为什么没有人提到性能问题?当你有多个基于这些不好处理的GUID的连接时,性能会变得非常差,我曾经遇到过这个问题 :(


1
你能详细说明一下吗?因为我正处于需要引入UUID(或类似)的情况,但我担心将它们用作主键。 - JoeTidee
2
UUIDs的大小只有整数的4倍(如果你的数据库支持UUID类型)。 - Jasen

15
主要优点是可以创建独特的id,无需连接数据库,并且id在全球范围内具有唯一性,因此您可以轻松地合并来自不同数据库的数据。虽然这些优点似乎很小,但在过去节省了我很多工作。
主要缺点是需要更多的存储空间(现代系统上不是问题),而且id实际上并不可读。调试时可能会有问题。
存在一些性能问题,例如索引碎片化。但这些问题很容易解决(comb guids by jimmy nillson: http://www.informit.com/articles/article.aspx?p=25862Edit合并了我对此问题的两个答案
@Matt Sheppard 我认为他的意思是您可以使用不同的GUID作为主键复制行。这是任何代理键的问题,而不仅仅是GUID。就像他所说的,通过向非键列添加有意义的唯一约束条件很容易解决。另一种选择是使用自然键,而这些键存在真正的问题...

15

@Matt Sheppard:

假设你有一个客户表。肯定不希望同一客户在表中出现多次,否则会在销售和物流部门产生很多混乱(特别是如果关于该客户的多行内容包含不同的信息)。

因此,你需要一个唯一标识符来唯一地识别每个客户,并确保该标识符已知于客户(在发票中),以便客户和客户服务人员在需要沟通时有一个共同的参考。为了保证没有重复的客户记录,可以通过在客户标识符上添加主键或通过在客户标识符列上添加 NOT NULL + UNIQUE 约束来向表中添加唯一性约束。

接下来,由于某种原因(我无法想象),要求你向客户表中添加一个 GUID 列并将其设置为主键。如果现在客户标识符列没有唯一性保证,那么你将在整个组织中引发未来的问题,因为 GUID 始终是唯一的。

某些“架构师”可能会告诉你,“哦,但我们在应用程序层处理了真正的客户唯一性约束!”。对于这种通用编程语言和(尤其是)中间层框架的流行趋势经常发生变化,并且通常不会超过你的数据库的寿命。而且很有可能在某个时刻,你需要访问数据库而不经过当前应用程序。==麻烦。(但幸运的是,你和“架构师”早已离开,所以你不必为清理混乱付出代价。)换句话说:请在数据库(以及其他层次,如果你有时间)中维护明显的约束。

换句话说:向表中添加 GUID 列可能有很好的理由,但请不要因此降低你对真正(==非 GUID)信息一致性的追求。


1
听听! 顺便说一下,我喜欢你的SQL比较页面。非常有用。唯一遗憾的是缺少变更日志。 - Henrik Gustafsson
5
我认为这个答案需要一些澄清:它假设UUID从来没有被用作主键。我不知道这个假设的来源,但我还没有见过一个不允许你将UUID用作主键的系统。我知道这是一个旧的答案,我猜当时可能没有广泛理解在分布式系统中使用UUID的好处。 - tne

12

如果GUID作为“唯一标识符”用于防止重复数据进入表中,可能会在未来给您带来很多麻烦。如果您想使用GUID,请考虑仍然在其他列上维护唯一约束。


11
问题的核心在于:引入全局唯一标识符(GUID)可以使每一行都独一无二。但是,行中的非人为部分可能会突然包含重复内容(即真相的几个版本)。 - Troels Arvin
8
+1来进行补偿。我理解你的意思,但表达得不太清楚。 - Stefano Borini

9

如果您将GUID用作主键并且还将该列用作聚集索引(这是相对常见的做法),则需要考虑的另一个小问题是插入时会受到影响。由于GUID的本质不是按顺序排列的,因此在插入时可能会出现页面分裂等情况。如果系统将具有高IO,则需要考虑这一点...


8

主键IDs vs GUIDs

GUID作为主键的代价(SQL Server 2000)

Myths, GUID vs. Autoincrement(MySQL 5)

这是您需要的内容。

UUID的优点

  • 对于每个表、每个数据库、每台服务器,它都是唯一的
  • 允许轻松合并来自不同数据库的记录
  • 允许将数据库轻松分布在多个服务器上
  • 可以在任何地方生成ID,而不必往返到数据库
  • 大多数复制场景都需要使用GUID列

GUID的缺点

  • 比传统的4字节索引值大4倍,如果不小心使用,可能会对性能和存储产生严重影响
  • 繁琐的调试过程(例如userid='{BAE7DF4-DDF-3RG-5TY3E3RF456AS10}')
  • 为了获得最佳性能(例如在SQL 2005上使用newsequentialid())并启用聚集索引,应该部分顺序生成GUID

4

有一件事情并没有得到很好的解决,那就是使用随机(UUIDv4)作为主键会对主键索引的性能造成负面影响。无论您的表是否围绕该键集群。

RDBMs通常确保主键的唯一性,并通过一个称为BTree的结构确保按键查找,这是具有大分支因子的搜索树(二叉搜索树的分支因子为2)。现在,顺序整数ID将导致插入仅在树的一侧发生,使大多数叶节点保持不变。添加随机UUID将导致插入在整个索引中分裂叶节点。

同样,如果存储的数据主要是时间性的,则通常情况下需要访问并联接最近的数据。对于随机UUID,模式将无法从中受益,并且会命中更多的索引行,从而需要更多的索引页面在内存中。对于顺序ID,如果最近的数据是最需要的,则热门索引页面需要更少的RAM。


这似乎对Postgres来说不是问题。https://www.postgresql.org/message-id/20151222124018.bee10b60b3d9b58d7b3a1839%40potentialtech.com - mackstann
@mackstann 感谢你提醒我,我记得最近看过一个页面,实际上有指标证明这并非如此。 - Antti Haapala -- Слава Україні
1
@mackstann,我找不到合适的链接,但这里有另一篇最近的文章,其中包含基准测试:https://www.depesz.com/2020/02/19/why-im-not-fan-of-uuid-datatype/ - Antti Haapala -- Слава Україні

2

优点:

  • UUID值在表和数据库之间是唯一的。这就是为什么可以在两个数据库或分布式数据库之间合并行。
  • 与整数类型数据相比,UUID通过url传递更加安全。如果通过url传递UUID,则攻击者无法猜测下一个id。但如果我们传递整数类型(例如10),则攻击者可以猜测下一个id是11、12等。
  • UUID可以离线生成。

1
到目前为止还没有提到的一件事:UUID使数据更难以被分析。
至少对于Web应用程序来说,使用url中的id访问资源是很常见的,比如像stackoverflow.com/questions/45399这样。如果id是一个整数,那么这个操作同时提供了以下信息:
- 提供了有关问题数量的信息(即2008年9月5日,第45399个问题被提出)。 - 提供了一个迭代问题的杠杆点(当我将其加1时会发生什么?我打开下一个被提出的问题)。
从第一个观点出发,我可以结合问题的时间戳和编号来分析问题的频繁程度及其随时间的变化情况。在像Stack Overflow这样提供公开信息的网站上,这可能影响较小;但在其他情况下,这可能会泄露敏感信息。
例如,我是一家向客户提供权限门控门户的公司,该门户的地址为portal.com/profile/{customerId}。如果id是一个整数,那么你可以定期查询lastKnownCustomerCount + 1,检查结果是否是404 - NotFound(客户不存在)或403 - Forbidden(客户存在,但您无权查看)来分析客户数量。
UUID的非顺序性可以减轻这些问题的影响。虽然这不能完全防止分析,但它已经是一个好的开始。

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