【问题标题】:SQL Server - Guid VS. LongSQL Server - Guid VS。长
【发布时间】:2010-11-13 08:38:53
【问题描述】:

到目前为止,我一直在使用 C#“Guid = Guid.NewGuid();”使用 Linq to SQL 生成一个唯一 ID 的方法,该 ID 可以存储为我的一些 SQL Server 数据库表中的 ID 字段。 我被告知,出于索引的原因,使用 GUID 是一个坏主意,我应该使用自动递增的 Long 来代替。使用 long 会加速我的数据库事务吗?如果是这样,我该如何生成 Long 类型的唯一 ID?

问候,

【问题讨论】:

  • “我被告知,出于索引的原因,使用 GUID 是个坏主意” - 如果您的身份字段上有聚集索引,则 GUID 会损害插入的性能。 SQL Server 根据聚集索引按顺序存储表。由于新的 GUID 是非顺序的,插入新行将不得不插入到表的中间而不是末尾,从而导致大量 IO。只要是聚集索引的列类型就会避免这个问题。
  • 只是添加 - 如果 GUID 是主键,那么默认情况下 GUID 将是集群的 - 这可以是非集群的,但需要手动干预
  • 请参阅 sqlblogcasts.com/blogs/martinbell/archive/2009/05/25/… - 和 dotnetrocks.com/default.aspx?showNum=455 了解有关该主题的大量内容的播客。

标签: c# sql sql-server guid long-integer


【解决方案1】:

两者各有利弊,这完全取决于您如何使用它们。

马上,如果您需要可以跨多个数据库工作的标识符,则需要 GUID。 Long 有一些技巧(手动为每个数据库分配不同的种子/增量),但这些技巧不能很好地扩展。

就索引而言,如果索引是聚集的,Long 将提供更好的插入性能(默认情况下主键是聚集的,但这可以为您的表修改),因为表不需要在每次插入。

然而,就并发插入而言,Long(标识)列将比 GUID 慢 - 标识列生成需要一系列排他锁以确保只有一行获得下一个序列号。在许多用户一直插入许多行的环境中,这可能会影响性能。这种情况下的 GUID 生成速度更快。

存储方面,GUID 占用的空间是 Long 的两倍(8 字节 vs 16)。但是,如果 8 个字节会显着影响一个叶子中容纳多少条记录,以及因此在平均请求期间从磁盘中提取的叶子数量,这取决于您的行的整体大小。

【讨论】:

  • 另外,如果您使用 GUID 作为集群键,那么该值(是 BIGINT 的两倍)也会添加到每个非聚集索引中的每个条目 - 从而增加浪费空间,不仅在磁盘上,而且在 SQL Server RAM 中。
【解决方案2】:

“索引女王” - Kim Tripp - 基本上都在她的索引博客文章中说明了一切:

基本上,她的最佳实践是:最佳聚类键应该是:

  • 独一无二的
  • 稳定(永不改变)
  • 不断增加

GUID 违反了“小”和“不断增加”的原则,因此不是最优的。

PLUS:您的所有集群键都将添加到每个非聚集索引中的每个条目(作为在数据库中实际查找记录的查找),因此您希望它们尽可能小可能(INT = 4 字节与 GUID = 16 字节)。如果您有数亿行和多个非聚集索引,则选择 INT 或 BIGINT 而不是 GUID 可能会产生重大影响 - 甚至只是空间方面。

马克

【讨论】:

  • 如果您担心规模,请不要选择 GUID。在较小的数据库中,从速度或规模的角度来看,可能没有太大的区别,但是如果您进入 1 亿多行表,并且有多个索引,GUID 就是性能噩梦......阅读上面 Kim Tripp 的文章,这是一个一个巨大的决定,让我们付出了 3 个月的技术债务
  • 嗨@mar​​c_s。它的老问题。但是您决定在过去的项目中选择什么?你使用 GUID 吗?还是随机 bigint 生成的密钥?
  • @deveton:我尽量避免 GUID——它们往往会对性能产生负面影响。我曾经并且仍然非常喜欢使用 INT IDENTITYBIGINT IDENTITY 作为我的代理主键。
  • 关于随机 bigint?如何确保它永远不会重复... GUID 在地球上永远不会重复。但也许随机 int/bigint 会重复?
  • @deveton: 不,不是随机的 - IDENTITY 提供不断增加的值,这些值在设计上保证是唯一的 - 只要你让它们存在并且不要摆弄身份列....
【解决方案3】:
【解决方案4】:

long(sql server 中的 big int)是 8 个字节,Guid 是 16 个字节,因此您将 sql server 在查找时必须比较的字节数减半。

要生成长整数,请在数据库中创建字段时使用 IDENTITY(1,1)。

所以要么使用 create table 或 alter table:

Field_NAME BIGINT NOT NULL PRIMARY KEY IDENTITY(1,1)

查看 cmets 将 Linq 发布到 sql

【讨论】:

  • 当您使用 L2S 将新记录提交到数据库时,不要填充 PK。它将由 L2S 作为提交的一部分填充,因此您可以在提交后从您的记录对象中检索它,如果您需要这样做。
  • 有没有办法使用 SQL Server 设计视图来做到这一点?我的 SQL 脚本代码不太好........
  • 有,但是不知道怎么弄。对不起:(
【解决方案5】:

当您需要考虑导入/导出到多个数据库时,请使用 guid。在处理具有多个子关​​系的数据集时,Guid 通常比指定 IDENTITY 属性的列更易于使用。这是因为您可以在与数据库断开连接的状态下在代码中随机生成 guid,然后一次提交所有更改。当 guid 生成正确时,它们很难被偶然复制。对于标识列,您通常必须在添加子数据之前对父行进行初始插入并查询其新标识。然后,您必须使用新的父身份更新所有子记录,然后再将它们提交到数据库。孙辈也是如此,以此类推。它建立了许多看似不必要和平凡的工作。您可以通过在没有 IDENTITY 规范的情况下计算随机整数来执行类似于 Guid 的操作,但是随着时间的推移插入更多记录,冲突的可能性会大大增加。 (Guid.NewGuid() 类似于随机 Int128 - 尚不存在)。

对于不会更改的小型查找列表或不会在多个数据库之间复制的数据,我使用 Byte (TinyInt)、Int16 (SmallInt)、Int32/UInt16 (Int)、Int64/UInt32 (BigInt)。 (权限、应用程序配置、颜色名称等)

我想无论您使用的是 guid 还是 long,索引都需要同样长的时间来查询。无论如何,在索引的表中通常存在大于 128 位的其他字段(例如,用户表中的用户名)。 Guids 和 Integers 之间的区别在于内存中索引的大小,以及填充和重建索引的时间。大多数数据库事务经常是读取。写作很少。首先专注于优化从数据库读取,因为它们通常由未正确优化、不正确的分页或缺少索引的连接表组成。

与任何事情一样,最好的办法就是证明你的观点。创建一个包含两个表的测试数据库。一个具有整数/长整数的主键,另一个具有 guid。用 N-Million 行填充每个。在 CRUD 操作(创建、读取、更新、删除)期间监控每个操作的性能。您可能会发现它确实对性能有影响,但微不足道。

服务器通常在没有调试环境的机器上运行,其他应用程序会占用 CPU、内存和硬盘驱动器的 I/O(尤其是 RAID)。开发环境只会让您了解性能。

【讨论】:

    【解决方案6】:
    【解决方案7】:

    您可以整天争论 GUID 或身份。我更喜欢数据库生成具有标识的唯一值。如果您合并来自多个数据库的数据,请添加另一列(以标识源数据库,可能是 tinyint 或 smallint)并形成复合主键。

    如果您确实使用身份,请务必根据您将生成的预期密钥数量选择正确的数据类型:

    bigint - 8 Bytes - max positive value: 9,223,372,036,854,775,807  
    int    - 4 Bytes - max positive value:             2,147,483,647
    

    注意“预期键数”与行数不同。如果您主要是添加和保留行,您可能会发现一个 INT 足以拥有超过 20 亿个唯一键。我敢打赌你的桌子不会那么大。但是,如果您有一个不断添加和删除行的大容量表,那么您的行数可能会很低,但您会快速通过键。你应该做一些计算,看看通过 INT 的 20 亿个密钥需要多少日志。如果它不会很快用完它们,请使用 INT,否则将密钥大小加倍并使用 BIGINT。

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 2011-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多