【问题标题】:Primary Keys - Native, Sequence, or GUID keys?主键 - 本机、序列或 GUID 键?
【发布时间】:2009-07-21 00:27:17
【问题描述】:

阅读thisthis,然后阅读this(讽刺地引用了另外两个),我发现自己想知道这个话题的讨论有多大?我是一名 SQL Server 人员,因此我倾向于使用以 int 形式自动生成的标识。但是,当我知道我需要在服务器和服务器之间进行某种形式的复制或在客户端和服务器之间进行同步时,我倾向于使用 GUID 作为我的密钥。

问题:我是否应该始终使用 GUID 作为所有表的主键,以防万一我可能需要这种可能的可扩展性?这是否使我的架构更加灵活,因为它可以随时在平台之间迁移?通过不嵌入特定于平台的功能,这是否有助于我保持 ORM 的灵活性(无论其风格如何)?

回应:

@David Archer:根据您的评论,我更新了我的帖子,不再说“天然钥匙”。你是正确的,自然键被定义为such。谢谢指正。

【问题讨论】:

  • 只是一点点,你的术语似乎有点不对劲。 “自然键”通常基于您所描述的类型记录的特定内容,并且被认为是唯一且不变的。想想用于存储美国公民列表的社会安全号码或用于用户列表的电子邮件地址。您正在寻找的术语是代理键,它同样适用于自动生成的整数和 GUID。

标签: orm primary-key database-agnostic design-decisions


【解决方案1】:

我更喜欢应用程序生成的主键,通常使用 NHibernate 实现的 lo/hi 算法(当我在项目中使用它时)。否则,顺序 GUID 也可以正常工作。这不仅仅是我的建议,而是 several 伙计们 who have 做这整个开发工作的时间比我自己长得多。

我看到使用数据库生成的主键的问题是,您必须访问数据库才能获取这些标识值,而不是在将所有内容保存到数据库之前进行设置。由于这个事实,它通常也会破坏 NHibernate 中的工作单元模式。如果您没有在应用中使用 UoW 模式,那么显然这个缺点不适用。

如果您在 PK 中使用 GUID,您肯定希望使用顺序 GUID 来消除索引碎片。这也为您提供了另一张海报提到的“粗略排序顺序”,尽管我通常会有一个 DateInserted 列或类似的列。

加入 GUID 列 has been shown 与您的 4 字节整数相比具有相当小的性能开销,我敢说对于非大型数据集,性能差异是微不足道的。

自然钥匙是魔鬼的产物。 :)

【讨论】:

    【解决方案2】:

    您可能不应该使用原始 GUID 作为您的主键。这样做会导致数据大量碎片化。 SQL Server 有一个function 为您提供“顺序 guid”以帮助缓解此问题。深入讨论了这个话题here。另一个很好的讨论是here ...

    这显示了碎片的数量 对于随机向导非常重要 (建议“分片 百分比”应该接近于零 尽可能)。使用的页数 随机向导高出 40%,并且 每页上使用的空间量是 更少,因此磁盘空间 需要的会增加。

    【讨论】:

    • 在上面的答案中,Guid 的值没有命中数据库,但是如果生成顺序 guid 的函数是 t-sql 函数,那么您仍然在命中数据库。如果您推荐一个顺序 guid 并且它需要一个 db 命中,为什么不直接点击 DB 并获得一个序号。
    【解决方案3】:

    除非你知道你真的需要它(即用于多系统同步等),否则我会避免主键的 GUIDS。

    在 SQL Server 复制领域,复制表中的行会添加一个 guid 以实现唯一性,因此如果您有需要,以后很有可能建立这种设计。

    关于碎片,还要考虑磁盘空间的成本。如果您要少于 10,000 行(在表中),这可能不是一个大问题,但如果您的系统必须支持超过 10,000 行(在表中),您会发现性能和磁盘存储成本(以及索引碎片)使用 Big Ints(大整数)+ 标识(自动编号)可以更好地服务于体积。

    我会完全避免使用自然键 - 即使它们周围的逻辑发生变化的风险也会让恕我直言(例如,如果它们突然变得不唯一)。

    【讨论】:

      【解决方案4】:

      我支持大多数其他回答者的说法,即您应该避免将 GUID 作为 SQL Server 中的聚集键 - 如果您真的愿意,可以将它们用作主键,但不要将您的表聚集在上面。

      主键是唯一标识每一行的键的逻辑概念 - 在这里,GUID 是有意义的,因为它几乎可以保证是唯一的。

      但是聚簇键是一个物理概念,它对表中的行进行物理排序,由于它们的随机性,GUID 在这里不太适合。这将导致大量索引碎片,从而导致性能下降,即使您不断地一遍又一遍地重新组织索引(以及表数据)。

      此外,由于聚集索引键被用作查找表中的行的查找值,它也将被添加到表上每个非聚集索引的每个条目中,这里GUID(16 字节)与 INT(4 字节)的大小开始发挥作用 - 您可能会浪费大量空间来跟踪查找值。

      我所知道的关于主/聚簇索引和 GUID 的最佳讨论是 Kim Tripp 的两篇文章,她是 SQL Server 领域的索引女王 - 查看它们!

      她对聚集索引的最终要求是:小、稳定、独特,并且希望不断增加。 GUID 违反了其中两个(小且不断增加)。即使是 SQL Server 中的 NEWSEQUENTIALGUID() 函数生成的 GUID 也不是完全和真正的顺序 - 所以我也不会使用它们。

      马克

      【讨论】:

        【解决方案5】:

        我已经被“自然键”改变或复制太多次了,以至于无法考虑使用它们。我是否使用序列或 GUID 作为键的决定取决于我是否希望阅读或说出其中之一。

        【讨论】:

        • 序列的缺点是可以被猜到。所以有人可能会编辑 URL 以查看它返回的记录。
        • 如果有人可以通过编辑 URL 导航到被禁止的页面,那么您的访问安全性就会被破坏。使用 GUID 并不能解决问题。
        【解决方案6】:

        我对此没有太多经验,但使用 GUID 加入让我感到畏缩。 4 个字节与 36 个字节看起来很恶心。

        但是,我已经开始使用 GUID 作为公共标识符,而不是身份字段本身。看看上面的 URL,1156712。如果由于某种原因,SO 必须与另一个类似的应用程序(比如 SU)合并,这些问题 id 会发生冲突,或者另一个必须更改它的 URL,从而弄乱任何硬编码链接,并且可能谷歌统计也是如此。而如果公开识别每个元素的方式是通过使用 GUID 并且内部连接使用 int 或 bigint 字段,那么您可以两全其美。

        使用这种方法仍然可以进行合并。如果发现冲突,可以即时生成新的内部标识符,而不会中断应用程序的其余部分。

        【讨论】:

        • GUID 是 32 个十六进制字符 == 16 个字节,而不是 36 个。:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-14
        • 1970-01-01
        • 2011-10-18
        相关资源
        最近更新 更多