【问题标题】:What are the reasons *not* to use a GUID for a primary key?*不*将 GUID 用作主键的原因是什么?
【发布时间】:2010-06-16 04:44:57
【问题描述】:

每当我设计数据库时,我都会自动为每个表(查找表除外)自动生成 GUID 主键

我知道我永远不会因为重复键、合并表等而失眠。对我来说,从哲学上讲,任何给定记录在所有域中都应该是唯一的,并且这种唯一性应该以一致的方式表示表到表。

我知道它永远不会是性能最高的选择,但抛开性能不谈,我想知道是否有反对这种做法的哲学论据?

根据回复让我澄清一下:

我说的是始终使用 GUID 代理键作为主键 - 无论是否以及如何在表上设计任何自然键或顺序键。这些是我的假设:

  1. 可以设计基于自然键的数据完整性,但不能假设。
  2. 主键的功能是参照完整性,与性能、顺序或数据无关。

【问题讨论】:

    标签: sql database-design relational-database


    【解决方案1】:

    GUID 似乎是您的主键的自然选择 - 如果您真的必须,您可能会争辩将它用于表的主键。

    我强烈建议不要这样做是使用 GUID 列作为 集群键,这是 SQL Server 默认执行的,除非您明确告诉它不要这样做.造成这种情况的主要原因确实是性能,它会在你的道路上咬你......(相信我,它会 - 只是时间问题) - 再加上资源的浪费(你的 SQL Server 中的磁盘空间和 RAM机器),这真的没有必要。

    你真的需要把两个问题分开:

    1) 主键 是一种逻辑结构 - 候选键之一,可唯一且可靠地标识表中的每一行。这可以是任何东西,真的 - 一个 INT、一个 GUID、一个字符串 - 选择对你的场景最有意义的东西。

    2) clustering key(在表上定义“聚集索引”的一列或多列)-这是一个与存储相关的物理事物,这里,一个小的、稳定的、不断增长的数据类型是您的最佳选择 - INT 或 BIGINT 作为您的默认选项。

    默认情况下,SQL Server 表上的主键也用作集群键 - 但不需要这样!将以前基于 GUID 的主键/群集键分解为两个单独的键 - GUID 上的主(逻辑)键和单独的 INT IDENTITY(1, 1)列。

    正如Kimberly Tripp - 索引女王 - 和其他人多次声明的那样 - 作为集群键的 GUID 不是最佳的,因为由于它的随机性,它会导致大量页面和索引碎片,并一般表现不佳。

    是的,我知道 - 在 SQL Server 2005 及更高版本中有 newsequentialid() - 但即使这样也不是真正的完全连续的,因此也会遇到与 GUID 相同的问题 - 只是不太突出。

    然后还有一个问题需要考虑:表上的聚簇键也将添加到表上每个非聚簇索引的每个条目中 - 因此您确实希望确保它尽可能小.通常,具有 2+ 十亿行的 INT 对于绝大多数表来说应该足够了 - 与作为集群键的 GUID 相比,您可以在磁盘和服务器内存中节省数百兆字节的存储空间。

    快速计算 - 使用 INT 与 GUID 作为主键和聚类键:

    • 具有 1'000'000 行的基表(3.8 MB 与 15.26 MB)
    • 6 个非聚集索引(22.89 MB 与 91.55 MB)

    总计:25 MB vs. 106 MB - 这只是在一张桌子上!

    更多值得深思的东西 - Kimberly Tripp 的优秀作品 - 阅读,再阅读,消化!这是 SQL Server 索引的福音,真的。

    马克

    【讨论】:

    • @marc_s - 我还没有看到任何关于 COMB guid(不是 newsequentialid)与整数作为聚集键的分析。是的,它占用了更多空间,但坦率地说,空间并不是大多数数据库系统的主要限制因素。
    • @THomas:它会占用更多空间,而且只是在磁盘上 - 还包括在服务器的主内存 (RAM) 中 - 很多人不这样做考虑到! RAM 并不像磁盘空间那么便宜,更多的空间 = 更多的 I/O = 更低的性能(通用和简化)
    • @marc_s - 在所有表上添加一个 int 聚集键(连同一个 GUID pk)不会解决合并数据库的问题。您仍然会处理同步身份列的可怕噩梦。此外,您将占用 20 个字节的索引空间,而不是仅使用 guid PK 的 16 个字节,因为您需要对 guid 列进行唯一约束。我不相信您永远不必使用的额外 int 集群密钥比选择代理 pk 策略(comb guids 或 int pks)更好。
    • @marc_s - 同样,4-8 GB 系统上的 25MB 与 106MB 是小菜一碟,假设您的表有一百万行。
    • @marc_s - 是的,我认为这是一个很好的观点,谢谢 - 虽然我仍然会把它放在表演中,而不是哲学上,阵营
    【解决方案2】:

    Jeff Atwood 详细讨论了这一点:
    http://www.codinghorror.com/blog/2007/03/primary-keys-ids-versus-guids.html

    Guid 优点:
    每个表、每个数据库、每个服务器都是唯一的
    允许轻松合并来自不同数据库的记录
    允许跨多个服务器轻松分布数据库
    您可以在任何地方生成 ID,而不必往返于数据库
    大多数复制方案都需要 GUID 列

    指导缺点:
    它比传统的 4 字节索引值大 4 倍;如果您不小心,这可能会对性能和存储造成严重影响
    调试麻烦(其中 userid='{BAE7DF4-DDF-3RG-5TY3E3RF456AS10}')
    生成的 GUID 应该是部分顺序的,以获得最佳性能(例如,SQL 2005 上的 newsequentialid())并启用聚集索引

    【讨论】:

    • 如果您不进行复制,那么我从未见过使用 GUID 来合并记录,因为通常最重要的是内容。此外,由于外键,我很少合并,我希望能够定期合并。如果我的数据库已分发或将被复制,我会添加它们,否则我依赖日期时间戳。
    • 任何不在您的处理器本机 int 大小中的东西对于索引操作都会运行得非常慢。还必须考虑到 GUIDS 生成通常必须锁定比常规本地序列更新更全局的东西。
    • 第一个链接不错,我会添加 krow.livejournal.com/497839.html
    • @Yarin,表演营越来越不重要了?你在开玩笑吗?性能对数据库至关重要。这是仅次于数据完整性的第二重要的事情。
    • @HLGEM- 我的意思是,GUID 与 INT 的选择与数据库性能的相关性越来越小 - 由于硬件/软件的改进 - 并不是说​​性能本身就不太好相关的。
    【解决方案3】:

    添加到 ewwwn:

    优点

    • 这使得开发人员几乎不可能“意外”向用户公开代理键(与几乎所有时间都发生的整数不同)。
    • 使合并数据库比处理标识列简单几个数量级。

    缺点

    • 更胖。它变胖的真正问题是它占用了每页更多的空间,并且索引中的更多空间使它们变慢。坦率地说,Guids 的额外存储空间在当今世界是无关紧要的。
    • 您绝对必须小心创建新值的方式。真正的随机值不能很好地索引。您不得不使用 COMB guid 或一些向 guid 添加顺序元素的变体。

    【讨论】:

    • “真正的超级唯一值”:INT IDENTITY 可能是您可以获得的最大唯一性——它们处理集群键的效果非常好。导致问题的不是 GUID 的唯一性,而是随机性。
    • @marc_s - 我选词不当。我所说的超级独特是指跨越时间和空间。 “随机”是一个更贴切的术语,我会调整。
    • 在我看来,在合并数据库时创建重复实体变得容易几个数量级——不好。不过,我喜欢让代理人更难暴露的观点:)
    • @onedaywhen - RE:创建副本,如果您的意思是创建数据本身的副本,那是另一回事。无论您使用何种代理键策略,您必须在表中的其他列上有一个业务键,原因与数据库合并无关。
    • 还有一点:如果您完全避开代理键并始终使用自然键,那么就不可能向用户公开代理键:)
    【解决方案4】:

    您仍然实现每个表的自然键,不是吗? - 单独的 GUID 键显然不会防止重复数据、冗余和随之而来的数据完整性丢失。

    假设您确实强制执行其他键,然后毫无例外地向每个表添加 GUID,这可能只是增加了不必要的复杂性和开销。它并没有真正使合并不同表中的数据变得更容易,因为无论如何您仍然必须修改/删除表的其他键。我建议您应根据具体情况评估 GUID 代理的使用。没有必要为每个表制定一揽子规则也没有帮助,因为毕竟每个表都建模了不同的东西。

    【讨论】:

    • 同意作为一名数据库设计人员,您始终需要根据具体情况评估和实施数据完整性,并且应该警惕一揽子规则。但在我看来,GUID 代理项提供了一种通用接口方法来识别记录唯一性是有价值的。我们可以尝试强制执行数据完整性,但不应该假设它,GUID 密钥至少每次都可以提供万无一失的记录唯一性,即使违反了自然密钥规则。
    • 所有键都是“万无一失的”,如果它们是通过唯一性约束强制执行的。我不能同意我们不应该假设数据完整性!数据库设计者的首要职责是创建正确的数据模型——确保以一种避免不准确结果的方式记录有关业务的相关事实。除非并且直到您实现自然键,否则您还没有实现。向导不会帮助您。
    • @David- 我通常对假设任何事情都持谨慎态度,更不用说真实世界的数据完整性了——一旦软件开发人员和未来的设计师/管理员能够使用/滥用数据库,这是不可能的。原作者兑现了他的401k。自然键不能是万无一失的,因此代理键的吸引力是任意的,即不依赖于数据 - 所以我猜我是在谈论保证参照完整性,而不是假设数据完整性
    • “无法预测软件开发人员和未来的设计师/管理员将如何使用/滥用数据库”。包括删除 GUID 列。如果数据没有完整性,那么参照完整性在很大程度上是没有意义的。在自然键上创建唯一键约束/索引的重点是 DBMS 将强制由该键标识的记录的唯一性 - 您不是假设现实世界的数据完整性,而是 在数据库中强制执行
    • @Yarin,我同意马克的观点。如果您还没有强制执行自然键,那么您实际上并没有强制执行参照完整性。通过允许重复数据,您正在创建删除异常的可能性,这意味着引用行实际上可能指向错误版本的数据,而应该被引用的行被删除。执行业务用户感兴趣的密钥应该先于 RI,而且通常更为重要。
    【解决方案5】:

    简单的答案:这不是关系。

    记录(由 GUID 定义)可能是唯一的,但没有任何关联的属性可以说是与该记录唯一发生的。

    使用 GUID(或 any 纯代理键)与将平面文件声明为关系文件一样,并没有更多的关系,因为每条记录都可以通过其行号来标识。

    【讨论】:

      【解决方案6】:

      一个潜在的重要原因,但往往没有想到的是,您将来是否可能必须提供与 Oracle 数据库的兼容性。

      由于 Oracle 没有 uniqueid 列数据类型,当您在两个不同数据库中为同一个主键有两种不同数据类型时,尤其是在涉及 ORM 时,这可能会导致一些噩梦。

      【讨论】:

      • 有趣——所以 Oracle 对所有主键都使用整数?
      • 我确信 Oracle 可以使用任何你喜欢的东西作为主键......只是他们没有那种特殊的类型来原生地表示 GUID。
      【解决方案7】:

      我想知道为什么没有标准的“miniGUID”类型?似乎在 GUID 上执行一个像样的哈希应该产生一个 64 位数字,在任何没有十亿或更多事物的宇宙中,该数字的重复概率很小。由于使用大多数 GUID/miniGUID 标识符的宇宙永远不会超过一百万,更不用说十亿了,我认为更小的 8 字节 miniGuid 会非常有用。

      当然,这并不意味着它应该被用作聚集索引;这将极大地影响性能。尽管如此,一个 8 字节的 miniGUID 只会浪费一个完整 GUID 的三分之一空间(与 4 字节的索引相比)。

      【讨论】:

        【解决方案8】:

        我可以看到给定应用程序或企业自己的标识符是唯一的并且在所有其自己的域中以一致的方式表示的情况(即,因为它们可能跨越多个数据库)但是出于这些目的,GUID 太过分了。我猜它们很受欢迎,因为它们开箱即用,设计和实施“企业密钥”需要时间和精力。设计人工标识符时的规则是使其尽可能简单,但不能更简单。 IDENTITY 太简单了,GUID 不够简单。

        存在于应用程序/企业之外的实体通常具有由外部可信来源维护的自己的标识符(例如,汽车有 VIN,书有 ISBN 等),在这种情况下,GUID 不会添加任何内容。所以我想我在这里的哲学论点是在 every 表上使用人工标识符是不必要的。

        【讨论】:

        • 我会告诫不要依赖外部标识符。当您第一次必须输入带有 VIN 字段的被追回的被盗车辆,或者您的收藏中没有 ISBN 的自行出版的“书”时,您将走下“制作自己的 VIN, ISBN,...”条目。
        • 如果自出版的“书”与出版的书有不同的标识符,那么它们就不会存在于同一个基表中(可能组合在 VIEW 中)。
        • @Peter Stuer:是否有人工标识符的案例?是的。每个表都需要人工标识符吗?没有。
        • 我对此投了反对票(Nvmd 我不能,没有足够的代表),因为您正在对不适用于现实世界的域完整性做出假设。假设一条记录只在其当前域内被引用是不切实际的。
        • @Yarin:我想你没抓住我的意思:如果要在应用程序/实体之外识别实体,那么它已经拥有 UPC/EAN/ISBN/VIN 品种的通用标识符由受信任的来源,因此 GUID 是多余的,或者您的应用程序是受信任的来源,因此 GUID 不是密钥的最佳格式。
        猜你喜欢
        • 2017-07-03
        • 1970-01-01
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        • 2017-04-10
        • 2012-05-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多