【问题标题】:Unique identifier (guid) as primary key in database design唯一标识符(guid)作为数据库设计中的主键
【发布时间】:2012-04-01 09:29:27
【问题描述】:

我们的数据存储在 SQL Server 2008 数据库中,表之间会有很多查询和连接。我们在团队内部有这个论点,有些人认为使用整数标识对性能更好,有些人认为使用 guid(唯一标识符)。

使用 GUID 作为主键,性能真的会受到那么严重的影响吗?

【问题讨论】:

  • 如果您对聚集索引进行 PK,UNIQUEIDENTIFIER 会出现最大的性能和碎片问题
  • 所以这很重要,那么说总是使用 int 而不是 guid 作为 pk 是真的吗?那为什么大家都用guid呢?
  • 看看这个链接看看使用UNIQUEIDENTIFIERsqlskills.com/blogs/paul/post/…对碎片的影响。另一方面,很少有人在聚集索引上使用UNIQUEIDENTIFIER
  • 我经常在聚集索引上使用它们。碎片问题是由于计算新值的方式造成的,而不是由于uniqueidentifier 数据类型本身。如果您对整数 ID 使用随机数,您将遇到同样的问题。使用NEWSEQUENTIALID() 或类似 COMB 的方法,它不应该是一个真正的问题。
  • 随机 GUID 上的聚类实际上可以提高性能,这与使用顺序 GUID 更好的普遍看法相反。 GUID 的随机性实际上可以减少最后一个数据页的争用,并显着提高高 I/O 系统上的插入性能。见:blog.kejser.org/2011/10/05/…

标签: sql-server database-design


【解决方案1】:

128 位 GUID (uniqueidentifier) 密钥当然比 32 位 int 密钥大 4 倍。但是,有几个关键优势:

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

最后,通过使用整数来挤出一些小的性能优势可能不值得失去 GUID 的优势。凭经验进行测试并自行决定。

就我个人而言,我仍然会根据具体情况同时使用这两种方法,但在我的情况下,决定因素从未真正归结为性能。

【讨论】:

  • +1 提到了 Comb,因为我读到这也大大减少了索引碎片。
  • Combs(即顺序 GUID)可能会减少碎片,但在高 I/O 系统上,RANDOM 非顺序 GUID 似乎实际上可以提高性能,尤其是对于插入。原因是页面拆分比尝试在最后一个数据页面上插入所有内容所引起的争用成本更低,就像顺序 ID 一样。见:blog.kejser.org/2011/10/05/…这真的取决于底层系统。
  • Guid as PK 如果它们是聚集的并且 PK 默认是聚集索引,则在插入时表现不佳,这意味着引擎将保持表(物理)有序并导致表拆分和重新排序。在 url 中公开 ID 没有任何有益的方式,如果它们是字符串、整数、guid 或其他什么都没有区别。向导不会混淆它。
  • @jean 如果您使用顺序 guid,插入性能不会“糟糕”。它与大 int 完全相同,仅大 8 个字节,这在 99.999999% 的情况下是无关紧要的
  • @AaronH 如果您使用 sequential 指导,性能损失不会像普通的无顺序的那样可怕。但是,是的,您的 PK 的“大小”有一个小问题,因为它会影响每页的行数,从而导致引擎在页面管理上更多地工作
【解决方案2】:

我个人将INT IDENTITY 用于我的大部分主键和集群键。

您需要将 主键 分开,这是一个逻辑结构 - 它唯一地标识您的行,它必须是唯一且稳定的 NOT NULL。 GUID 也适用于主键 - 因为它保证是唯一的。如果您使用 SQL Server 复制,则将 GUID 作为主键是一个不错的选择,因为在这种情况下,无论如何您都需要一个唯一标识 GUID 列。

SQL Server 中的clustering key 是一种物理构造,用于对数据进行物理排序,要正确处理要困难得多。通常,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 作为集群键 - 只需稍加了解即可。

【讨论】:

  • “GUID 对于集群键来说是一个非常糟糕的选择”与“我看到的最后一个测试表明 GUID 几乎一样快”......
  • @TOMMYWANG:常规 GUID NOWHERE NEAR 与 INT 一样快 - 参见 Kim Tripp 的 Disk space is cheap .... that's NOT the point!,对 INT 与 GUID 进行了一些测试
  • 泛化:“GUID 对于集群键来说是一个非常糟糕的选择,因为它很宽,完全随机,因此会导致索引碎片和性能不佳”。这是一个笼统的陈述,通常是正确的。但是,您是否假设 dba 知道忽略此建议不正确的情况?不幸的是,给出建议的环境尚不清楚。我知道你不能涵盖所有场景,但让我们在双曲线上稍微轻松​​一点。我见过一个场景,虽然是在另一个数据库上,但它使用集群分区 GUID 作为最佳实践。
【解决方案3】:

将 GUID 作为主键的最大问题是它们会导致大量表碎片,这可能是一个很大的性能问题(表越大,问题越大)。即使作为非聚集索引的键,它们也会导致索引碎片。

您可以通过设置适当的填充因子来部分缓解该问题——但这仍然是一个问题。

大小差异并没有给我带来太多困扰,除非在需要进行表扫描的其他窄行的表上。在这些情况下,每个 DB 页能够容纳更多行是一种性能优势。

使用 GUID 可能有充分的理由,但也有一定的成本。我通常更喜欢 INT IDENTITY 作为主键,但当 GUID 是更好的解决方案时,我不会避免使用它们。

【讨论】:

    【解决方案4】:

    使用 GUID 的主要优点是它们在所有空间和时间上都是唯一的。

    使用 GUID 作为键值的主要缺点是它们是 大的。每次弹出 16 个字节,它们是 SQL 中最大的数据类型之一 服务器。基于 GUID 构建的索引将比 建立在 IDENTITY 列上的索引,通常是整数(4 个字节)。

    因此,对于需要合并来自多个来源的数据的情况,它们是一个很好的解决方案

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

    【讨论】:

      【解决方案5】:

      如果数据库表记录可以增长到百万条记录,我觉得用它作为主键不是一个好主意。

      【讨论】:

      • 我不明白你回答背后的原因; GUID 在许多语言中非常频繁地用于表示唯一值。 ASP.NET 在其安全实现中大量使用它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 2011-08-29
      • 2011-06-19
      • 1970-01-01
      相关资源
      最近更新 更多