Guids与自增整数的比较

15

请问在处理代码中的父子对象ID时,是否有最佳编码实践?数据库记录使用自动递增的整数作为ID(在初始保存时)。当然,在代码中,您无法猜测这个ID可能是什么,因此必须将其留空,并预计在事务中保存所有这些项,在获取父ID后将其设置在所有子项上然后再保存它们。

另一方面,Guids在代码中更容易处理,因为您可以愉快地先生成Id并将其设置在所有内容上,然后无需担心保存。

是否有一种好的简单方法来使用自动增量整数作为它们的数据库键来处理代码中的对象?

谢谢

2个回答

35

GUID(全局唯一标识符)可能会成为你的主键的自然选择,如果你非要这样做,你可能会争辩说将其用作表的PRIMARY KEY。但是我强烈建议不要将GUID列用作聚集键,因为SQL Server默认情况下会这样做,除非您明确告诉它不要这样做。

你需要分开两个问题:

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

2) 聚集键(定义表上“聚集索引”的列)- 这是一个物理存储相关的事情,在这里,稳定、增长缓慢的数据类型是最好的选择 - INT或BIGINT作为默认选项。

默认情况下,SQL Server表上的主键也用作聚集键 - 但这并不一定如此!我曾经亲眼看到分离先前基于GUID的主/聚集键带来的巨大性能提升 - 将主(逻辑)键放在GUID上,将聚集(排序)键放在单独的INT IDENTITY(1,1)列上。

正如索引女王Kimberly Tripp和其他人多次强调的那样,将GUID用作聚集键并不是最优的选择,因为由于其随机性,它会导致大量页面和索引碎片以及通常的性能问题。

是的,我知道 - SQL Server 2005及更高版本中有newsequentialid() - 但即使这样也不完全顺序,因此也会遇到与GUID相同的问题 - 只是稍微不太明显。

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

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

  • 基础表有100万行(3.8 MB vs 15.26 MB)
  • 6个非聚集索引(22.89 MB vs 91.55 MB)

总计:25 MB vs 106 MB - 这仅仅是在单个表上!

值得思考的更多内容 - 由Kimberly Tripp提供的优秀文章 - 读它,再读一遍,理解它!这是SQL Server索引的真谛。

同时: 从C# / .NET的角度来看 - 它取决于您如何访问SQL Server数据库。如果您使用像Linq-to-SQLEntity Framework v4这样的东西,您的.NET对象将自动更新插入的ID (从您的INT IDENTITY列) - 您根本不需要做任何事情。因此对我来说,这是您无需使用GUID的一个更少的原因.... GUID作为SQL Server集群键非常糟糕 - 不仅糟糕 - 真的、真正、非常糟糕。

非常有帮助,尤其是对于那些缺乏这些方面深入知识的人,比如我自己 :-) - diggingforfire
我已经犯了前任使用Guid作为聚簇键的错误,我自己不会再犯这个错误 :) 我没有使用linq-to-sql或entity framework..也许这值得一看。由于这些表并不会真正受到什么影响,也许Guid是未来的方向,只是试图避免除非我确实需要它们..因此询问是否有巧妙的方法使用ints..非常感谢您的有益答案和时间..不知道有没有人能从.NET方面更详细地回答这个问题..? - nat
1
+1,我正要解释为什么顺序ID对索引性能很重要。这是一个很好的解释。 - Sedat Kapanoglu
我从Tripp的文章中没有错过重点。在几乎所有情况下,顺序GUID对于主键来说都是完全合适的。 - Joe Strommen
@JoeStrommen:即使是“伪顺序”的GUID,仍然比INT大四倍,因此会膨胀您的表格(如果您有大量行和几个NC索引)。另外:大多数情况下,程序员选择GUID,因为这些可以在插入数据库之前在客户端上生成 - 而这些绝对不是连续的! - marc_s

2
GUID相对于自增ID有几个“编码”上的优势。
首先,GUID是解耦的,您不需要访问数据库即可获取几乎唯一的ID。
因此,您可以在内存中创建一个新记录,使其现在就知道并将实际存储传递给某个服务,然后愉快地使用它来添加相关的本地数据,随后将其传递给同一或另一个服务。EF等工具会帮您处理要将记录插入数据库,然后获取DBMS分配给其的标识以传递给下游功能的问题。如果您拥有另一个唯一键并且自增ID是代理键,则可以避免此问题,但这不是免费的。
如果我没有进行分布式处理并且我的应用程序必须连接到数据库,并且自增ID是真正的代理键(而不是公开为订单号或类似的),并且int类型覆盖了范围,并且不存在两个或多个客户想要合并其数据库的情况,那么我不会使用GUID。
处理一个GUID是另一个问题,就我而言,这些问题并不重要。与处理非唯一键相比,这些问题都是微不足道的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接