4

因此,我和一位同事正在争论哪种方式更适合生成作为 GUID 的主键。

我们将 .NET 4.0 与 Entities 4 一起使用,并使用存储的过程来进行选择/插入/更新。

他想在代码中创建 GUID 主键,并使用 Guid 类或/和使用一些创建的 Sequential GUID 类将其作为插入的一部分传回。

我希望 SQL Server 在插入时使用 newid() 或 newsequentialid() 创建 GUID。

我反对他的方式的论点是,如果您必须进行多次插入,则必须进行往返以获取每个插入的 guid,以便为外键约束保持这种关系。另外,使用这种方式,您必须为每个插入进行多次往返。

他关于使用 SQL 执行的论点是,在插入发生之前他无法访问密钥,并且必须等待插入发生才能将主键 guid 重新用于代码的其他部分。这样,您可以与存储过程建立一个连接,并处理所有插入。

那么,哪种方法更适合单次插入?哪种方法更适合事务中的多次插入?

4

2 回答 2

12

GUID 似乎是您的主键的自然选择 - 如果您真的必须,您可能会争辩将它用于表的 PRIMARY KEY。我强烈建议不要使用 GUID 列作为clustering key,SQL Server 默认会这样做,除非您明确告诉它不要这样做。

您确实需要将两个问题分开:

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

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

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

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

是的,我知道 -newsequentialid()在 SQL Server 2005 及更高版本中有 - 但即使这样也不是真正的和完全顺序的,因此也会遇到与 GUID 相同的问题 - 只是不那么突出。如果您坚持使用GUID,那么至少使用newsequentialid()服务器上的方法!

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

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

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

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

更多值得深思的东西——金伯利·特里普(Kimberly Tripp)的优秀作品——读一读,再读一遍,消化一下!这是 SQL Server 索引的福音,真的。

马克

于 2011-04-07T15:49:57.560 回答
1

当我有这样的问题时,我会对自己说“SQL Server 擅长集合,所以让它做它擅长的事情”,有时“1 只是 N 的特定情况”。

哪种方法更适合单次插入?

对于同步 sql 调用的任何一种方法,单个插入时间都是相同的。然而,“他的”方法会给你带来更多的寻道时间问题,因为他的顺序 guid 方法不如 sql 服务器(你可能会失去全局唯一性)。当您不可避免地需要进行多次插入时,它也会拆分您的代码库。

哪种方法更适合事务中的多次插入?

如果您正在争论基于集合的插入 ( insert / select ) 与单行插入 (insert into),那么基于集合的插入将在多个插入中获胜,因为返回客户端的行程是昂贵的部分。

如果这是我,我将创建一个 SP,它采用要插入的对象的序列化集合,使用输出子句进行插入/选择,查看此页面上的“示例 B. 使用带有标识和计算列的 OUTPUT” ,让 sql服务器创建 GUID(如果您卡在它上面)并返回到客户端或在 SP 中运行下一条语句以根据您的插入生成的输出表插入子行。

于 2011-04-07T20:09:42.587 回答