在 C# 和 DB 中创建 Guid 键的区别
Posted
技术标签:
【中文标题】在 C# 和 DB 中创建 Guid 键的区别【英文标题】:Difference between creating Guid keys in C# vs. the DB 【发布时间】:2009-01-30 18:39:50 【问题描述】:我们使用 Guid 作为数据库中实体的主键。传统上,我们遵循让数据库在 INSERT 期间为实体设置 ID 的模式,我认为主要是因为这通常是您使用自动增量字段或其他方式处理事情的方式。
我越来越发现在对象构造期间在代码中进行键分配会更方便,主要原因有两个:
-
您知道,一旦对象的构造函数运行,它的所有字段都已初始化。您永远不会有“半生不熟”的物体。
如果您需要执行一批操作,其中一些操作取决于知道对象的键,您可以一次完成所有操作,而无需往返数据库。
是否有任何令人信服的理由不以这种方式做事?也就是说,当使用 Guid 作为键时,是否有充分的理由将键分配留给数据库?
编辑: 很多人对是否应该将 Guid 用于 PK(我知道)持有强烈的意见,但这并不是我问题的重点。
除了集群问题(如果您正确设置索引,这似乎不是问题),我还没有看到避免在应用层创建键的令人信服的理由。
【问题讨论】:
+1 好问题,我很好奇自己是否有任何缺点... 这就是 Guids 的美妙之处。您可以在数据库中保留自动分配,如果需要,仍然可以在代码中分配它,而不用担心它。 我讨厌向导。当您要管理的行数少于 2B 时,使用 int 有什么问题? 如果您坚持使用 GUID,我强烈建议在数据库端使用 SQL Server 2005 NEWSEQUENTIALGUID - 至少可以在一定程度上减轻索引碎片的负面影响。但我同意 StingyJack - PK 的 INT (IDENTITY) 有什么问题?? 【参考方案1】:我认为你在客户端创建它们做得很好。正如您所提到的,如果您让数据库这样做,您必须找到某种方法(真的想不出任何方法)来获取该密钥。如果您使用的是身份,则可以使用调用来获取为表创建的最新身份,但我不确定是否存在用于 guid 的调用。
【讨论】:
无论密钥的类型(即 int、CHAR、GUID 等)如何,在大多数数据库中检索刚刚添加的 PK 的过程都是相同的。 我认为您不应该代表“大多数数据库”。最多产的 Oracle 之一根本不是那样工作的。【参考方案2】:通过在 C# 中执行此操作,您可能会冒重新分配 GUID 并将其保存回数据库的风险。通过让数据库负责它,您可以保证这个 PK 不会改变,也就是说,如果您设置了适当的约束。话虽如此,您可以在 C# 代码中设置类似的约束,以防止在分配唯一 id 后更改它,但是您必须在所有应用程序中执行相同的操作......在我看来,在 C# 中使用它听起来比数据库更需要维护,因为数据库已经内置了防止更改主键的方法。
【讨论】:
不知道为什么你被改装了。我不认为认为您的代码中可能存在错误是不合理的。我认为一些 c#'ers 被这个建议冒犯了。 嗯,对不起,如果是这样的话,我是一个铁杆 C#'er 并且不打算这么说,我只是想说你一次性编写的约束和数据库代码放置并忘记它,而不是在几个 C# 应用程序中进行。 我没有downmod,但我认为第一句话不准确。两个不同的客户端生成相同 GUID 的可能性极低。 我不认为 Ricardo 的评论是关于在不同 C# 调用中生成相同 GUID 的可能性,而是关于应用程序编码与将索引功能集中在数据库中的变幻莫测。 不,但是有可能将 2 个不同的 GUID 分配给单行,这就是我阅读第一句话的方式。【参考方案3】:有趣的问题。
传统上我也使用数据库分配的 guid,但最近我正在开发一个 Windows Mobile 应用程序,而 SQL CE 数据库不允许使用 newguid,所以我必须在代码中完成。
我使用 SQL 复制将数据从移动设备获取到服务器。在过去的 6 个月中,我有 40 个 SQL CE 客户端将超过 100000 条记录同步回 SQL 2005 服务器,没有一个丢失或重复的 guid。
所需的额外编码可以忽略不计,在插入之前了解 guid 的好处实际上减少了一些复杂性。
我没有进行任何性能检查,所以除了性能之外,我看不出有任何理由不按照您的建议实施 guid 处理。
【讨论】:
【参考方案4】:GUID 的性能很糟糕
我会把它留在数据库中,特别是现在 SQL Server 有 NEWSEQUENTIALID(),它不会再导致插入页面拆分,因为值是随机的,创建的每个 NEWSEQUENTIALID 都将大于前一个...只有 caviat是只能作为默认值使用
【讨论】:
我猜,如果 GUID 是一个聚集索引,那么关于页面拆分的事情是正确的。 GUID 可能是 PK,但可能还有另一个(更自然的)列更适合用作聚集索引。 也许,但默认情况下,PK 是集群的,大多数人甚至没有意识到 :-( 参见***.com/questions/170346/… 在 C# 中创建顺序 guid。 GUID 并不是“性能糟糕”——这是一种严重的夸大其词。在 GUID 上 JOIN 的 SELECT 查询将比在 int 上 JOIN 的类似查询慢(GUID 连接与 CHAR 或 VARCHAR JOIN 一样慢)。 GUID 也比 int 占用更多空间,但这是微不足道的。 “多一点空间”是一种严重的轻描淡写。【参考方案5】:如果您必须在 GUI 之外进行插入(考虑从其他供应商导入数据或从您购买的公司导入数据并且必须与您的数据合并),则不会自动分配 GUID。这不是一个无法克服的问题,但仍然值得考虑。
【讨论】:
【参考方案6】:我让一个空的 Guid 作为一个指示符,表明该对象虽然已构建,但尚未插入(或从)数据库中检索。
【讨论】:
我不同意这种方法。如何确定属性已更改的插入对象。即将被删除的对象。 Guid.Empty 不是最好的方法。 必要时,我们在属于 New/UnModified/Modified/Deleted 之一的实体上使用单独的枚举字段来显式跟踪其在数据库中的状态。 @Ray aamof 我进行了选择以获取数据库中当前的内容:然后,将我拥有的内容与我刚刚选择的内容进行比较,可以告诉我哪些内容已被编辑或删除。【参考方案7】:正如 SQLMenace 所指出的,标准 GUID 会对索引和分页产生负面影响。在 C# 中,您可以使用一点 P/Invoke 的乐趣来生成像 NEWSEQUENTIALID() 这样的顺序 GUID。
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
通过这种方式,您至少可以继续使用 GUID,但在生成方式和生成位置方面获得更大的灵活性。
【讨论】:
【参考方案8】:好的,该插话了。我想说的是,生成的 GUID 客户端保存到数据库是最好的方法——前提是您碰巧使用 GUID 作为您的 PK,我只推荐一个场景:断开连接的环境。
当您使用断开连接的模型进行数据传播(即 PDA/手机应用程序、用于受限连接场景的笔记本电脑应用程序等)时,作为 PK 生成客户端的 GUID 是最好的方法。
对于其他所有情况,您最好使用自动增量身份 PK。
为什么?嗯,有几个原因。首先,通过使用跨行聚集 PK 索引,您确实获得了很大的性能提升。 GUID PK 和聚集索引不能很好地配合使用——即使使用 NEWSEQUENTIALID,顺便说一下,我认为它完全忽略了 GUID 的意义。其次,除非您的情况迫使您不要这样做(即您必须使用断开连接的模型),否则您确实希望将所有内容保持事务性并同时插入尽可能多的相互关联的数据。
【讨论】:
【参考方案9】:除了集群问题(如果您正确设置索引,这似乎不是问题),
作为索引的 GUID 总是会非常混乱 - 没有“适当的”设置可以避免这种情况(除非您在 SQL Server 引擎中使用 NEWSEQUENTIALGUID 函数)。
恕我直言,最大的缺点是大小 - GUID 是 16 字节,INT 是 4。PK 不仅存储在主键树中,而且还存储在每个非聚集索引条目上。
有几千个条目,这可能不会有很大的不同 - 但如果您有一个包含数百万或数十亿个条目和几个非聚集索引的表,则使用 16 字节 GUID 与 4 字节 INT 作为PK 可能会对所需的空间产生巨大的影响 - 磁盘和 RAM。
马克
【讨论】:
不过,我的问题不是“我们是否应该使用 GUID”(我们已经进行过讨论并使用了 GUID)。相反,我很好奇在应用层而不是 DB 层创建 guid 是否有任何缺点。 是的,我明白这一点 - 但我觉得你错过了一个重要的观点,强烈反对为你的密钥使用 GUID。 如果你真的必须坚持使用 GUID,那么是的,有一个 BIG PLUS 在数据库端创建它们 - 你可以使用 SQL Server 2005 的“NEWSEQUENTIALGUID”功能并在通过创建连续的(例如不断增加的)GUID,至少可以减轻碎片化的一些缺点。 如果您没有在您的 PK 上进行聚类,我仍然不清楚碎片问题出现在哪里。大概您应该根据您的方式将聚类在一个易于聚类的列上'将使用该表中的数据。 是的,如果您有非集群的 PK,那么为 PK 使用 GUID 问题不大。但是由于 PK = 集群键是默认的并且被绝大多数人使用,所以它通常是一个问题。以上是关于在 C# 和 DB 中创建 Guid 键的区别的主要内容,如果未能解决你的问题,请参考以下文章
C#中 BackGroundWorker与Thread的区别?