数据库设计中使用唯一标识符(GUID)作为主键

22
我们的数据存储在一个SQL Server 2008数据库中,表与表之间将有大量的查询和连接。我们团队内部有一个争论,有人主张使用整数身份作为主键可以获得更好的性能,而有些人则主张使用GUID(全局唯一标识符)。
使用GUID作为主键是否会严重影响性能?

2
如果您将UNIQUEIDENTIFIER作为主键并创建聚集索引,那么最大的性能和碎片化问题就会出现。 - Lamak
那么它确实很重要,总是使用int而不是guid作为主键,这是正确的吗?为什么每个人都使用guid呢? - TOMMY WANG
2
我经常在聚集索引上使用它们。碎片化问题是由于新值计算的方式导致的,而不是 uniqueidentifier 数据类型本身导致的。如果您为整数ID使用随机数字,则会遇到同样的问题。使用 NEWSEQUENTIALID() 或类似 COMB 的方法,就不应该是一个真正的问题。 - richardtallent
Richardtallent,您是否使用UniqueIdentifier与其他表进行连接? - TOMMY WANG
1
在随机GUID上进行聚类实际上可以提高性能,与使用顺序GUID更好的普遍观点相反。GUID的随机性实际上可以减少最后一个数据页上的争用,并显著增加高I/O系统中的插入性能。请参见:http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/ - Triynko
显示剩余4条评论
5个回答

39
一个128位的GUID(uniqueidentifier)密钥当然比一个32位的int键大4倍。不过有几个关键优点:
  • 合并内容时没有“IDENTITY INSERT”问题
  • 如果您使用COMB值而不是NEWSEQUENTIALID(),您将获得一个“免费”的INSERT时间戳。您甚至可以使用一些花哨的CAST()调用,在主键上基于日期/时间范围选择。
  • 它们是全局唯一的,这在某些情况下非常方便。
  • 由于无需跟踪高水位标记,因此您的BL层可以分配该值而不是SQL Server,从而消除了插入后获取主键的步骤SELECT scope_identity()。
  • 如果有可能有超过20亿条记录,则需要使用bigint(64位)而不是int。这样做后,uniqueidentifier只比bigint大两倍。
  • 使用GUID使在URL中公开密钥更加安全,而不会暴露自己受到“猜测ID”攻击的风险。
  • 由于SQL Server如何从磁盘加载页面以及现在处理器大多是64位,因此仅仅因为一个数字是128位而不是32位,并不意味着它需要4倍于比较时间。我看到的最后一个测试表明,GUID几乎与int一样快。
  • 索引大小取决于包括多少列。尽管GUID本身更大,但与索引中的其他列相比,额外的8或12个字节可能微不足道。

最终,通过使用整数来挤出一些小的性能优势可能不值得失去GUID的优势。根据实际情况进行测试并自行决定。

就我个人而言,在不同情况下我仍然会使用两者,但决定因素从未真正取决于性能。


3
+1是指赞成提到Comb方法,因为我读过这种方法也可以极大地减少索引碎片化。 - Martin
2
Combs(即连续GUID)可以减少碎片,但在高I/O系统上,似乎随机非连续GUID实际上可以提高性能,特别是对于插入操作。原因是页面分裂比尝试将所有内容插入到最后一个数据页引起的争用要便宜,就像使用连续ID一样。请参见:http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/ 这真的取决于底层系统。 - Triynko
2
如果将Guid作为主键并且它们是聚集索引,那么在插入时性能会非常差。默认情况下,主键是聚集索引,这意味着引擎将保持表(物理)有序,并导致表拆分和重新排序。没有任何有益的方法来在URL中公开ID,无论它们是字符串、整数、GUID还是其他类型。GUID不会混淆它。 - jean
@jean 如果你使用顺序 GUID,插入性能不会很差。它与 big int 完全相同,只是多了 8 个字节,在 99.999999% 的情况下都是无关紧要的。 - AaronHS
@AaronH 如果您使用顺序GUID,则性能损失不会像普通的非顺序GUID那样可怕。但是,确实存在一个关于您的PK“大小”的小问题,因为它会影响每个页面的行数,导致引擎在页面管理上多做一些工作。 - jean

25

我通常使用 INT IDENTITY 作为大多数主键和聚集键。

需要区分的是,主键 是一个逻辑结构,用于唯一标识行,必须是唯一且稳定的,同时不能为 NOT NULL。GUID 也可以作为主键,因为它保证唯一性。如果您使用 SQL Server 复制,那么将 GUID 作为主键是一个不错的选择,因为在这种情况下,您必须拥有一个唯一标识 GUID 列。

SQL Server 中的 聚集键 是一个物理结构,用于数据的物理排序,而且更难以正确设置。通常,SQL Server 索引之后的女王 Kimberly Tripp 还要求好的聚集键必须是唯一、稳定、尽可能窄,并且最好是单调递增的(所有这些都适用于 INT IDENTITY)。

请参阅她关于索引的文章:

此外,还可以看看 Jimmy Nilsson 的文章:The Cost of GUIDs as Primary Key

一个GUID作为聚集键是非常糟糕的选择,因为它宽,完全随机,因此会导致索引碎片和性能下降。此外,聚集键行也会存储在每个非聚集(附加)索引的每个条目中,因此您需要确保其尽可能小——GUID为16字节,而INT为4字节,在多个非聚集索引和数百万行的情况下,这将产生巨大的差异。
在SQL Server中,默认情况下,您的主键是聚集键,但它不必如此。您可以轻松地使用GUID作为非聚集主键,并将INT IDENTITY用作聚集键——只需要稍微注意一下即可。

1
"GUID作为聚集键是一个非常糟糕的选择" VS "我最近看到的测试表明,GUID几乎和其他键一样快"。 - TOMMY WANG
2
@TOMMYWANG:一个普通的GUID根本不如INT快 - 参见Kim Tripp的文章《磁盘空间很便宜……这不是重点!》(http://www.sqlskills.com/BLOGS/KIMBERLY/post/Disk-space-is-cheap.aspx),其中进行了一些INT vs. GUID的测试。 - marc_s
概括而言:“GUID是一个非常糟糕的聚集键选择,因为它很宽,完全随机,从而导致索引碎片化和性能差。” 这是一个经常正确的笼统说法。但是,如果有情况不适用于此建议,DBA会知道忽略它吗?不幸的是,给出建议的环境并不清楚。我理解您无法涵盖所有情况,但让我们在夸张的措辞上稍微轻松一点。我曾经看到过一种场景,尽管在另一个数据库上,使用聚集分区GUID作为最佳实践。 - phil soady

4
GUID作为主键的一个大问题是会导致表大量碎片化,这可能会成为性能问题(表越大,问题越严重)。即使作为非聚集索引的键,它们也会导致索引碎片化。
通过设置适当的填充因子可以在一定程度上缓解问题,但仍然存在问题。
虽然大小差异不太让我感到困扰,但对于其余行比较窄且需要进行表扫描的表而言,则能够在DB页面中容纳更多行是一种性能优势。
使用GUID的好理由确实存在,但也存在成本。通常我更喜欢使用INT IDENTITY作为主键,但如果GUID是更好的解决方案,则不会避免使用它们。

0
使用GUID的主要优点是它们在所有空间和时间上都是唯一的。但是,将GUID用作键值的主要缺点是它们非常大。每个GUID占用16个字节,是SQL Server中最大的数据类型之一。基于GUID构建的索引比基于通常为int(4个字节)的IDENTITY列构建的索引更大且更慢。

因此,当您需要合并来自多个来源的数据时,它们是一个很好的解决方案。

来源:http://www.sqlteam.com/article/uniqueidentifier-vs-identity


-1
如果数据库表的记录可以增长到百万条,我认为将其用作主键不是一个好主意。

1
我不理解你回答的推理; GUID在许多语言中被广泛用于表示唯一值。ASP.NET在其安全实现中大量使用它。 - user1945782

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