问题 在C#和DB中创建Guid密钥之间的区别


我们使用Guids作为数据库中实体的主键。传统上,我们遵循一种模式,让数据库在INSERT期间设置实体的ID,我想主要是因为这通常是你使用自动增量字段或其他任何东西处理事物的方式。

我发现在对象构建期间在代码中进行键分配更加方便,主要有两个原因:

  1. 你知道,一旦对象的构造函数运行,它的所有字段都已初始化。你永远不会有“半生不熟”的物体。
  2. 如果你需要做一批操作,其中一些操作依赖于知道一个对象的密钥,你可以一次完成所有这些操作而不需要往返于数据库。

有没有令人信服的理由  以这种方式做事?也就是说,当使用Guids作为密钥时,是否有充分的理由将密钥分配留给数据库?

编辑: 很多人都对Guid是否应该用于PK(我知道)有强烈的意见,但这不是我的问题。

除了集群问题(如果正确设置索引似乎没有问题),我还没有看到避免在应用程序层中创建密钥的令人信服的理由。


9706
2018-01-30 18:39


起源

+1好问题,我很好奇自己是否有任何方面的缺点...... - BFree
这是Guids的精彩之处。您可以在数据库中保留自动分配,并在必要时仍然可以在代码中分配它而不必担心它。 - NotMe
我讨厌guids。当你管理的行数少于2B时,int有什么问题? - StingyJack
如果 你坚持使用GUID,我强烈建议在数据库端使用SQL Server 2005 NEWSEQUENTIALGUID - 至少可以减少索引碎片的负面影响。但我同意StingyJack的说法 - 对于PK来说,INT(IDENTITY)有什么问题? - marc_s


答案:


我认为你在客户端创建它们做得很好。正如您所提到的,如果您让数据库执行此操作,您必须找到一些方法(不能想到任何真正的方法)来获取该密钥。如果您使用的是标识,则可以使用调用来获取为表创建的最新标识,但我不确定是否存在guid。


5
2018-01-30 18:44



无论密钥的类型如何(即int,CHAR,GUID等),检索刚刚添加的PK的过程在大多数DB中都是相同的。 - MusiGenesis
我不认为你应该为“大多数数据库”发言。最多产的Oracle之一,完全没有这样的工作。
@Mark当然会查找RETURNING INTO子句 - Ricardo Villamil


通过在C#中执行此操作,您可能会冒着重新分配GUID并将其保存回数据库的风险。通过让数据库对其负责,您可以保证这个PK不会改变,也就是说,如果您设置了适当的约束。话虽如此,您可以在C#代码中设置类似的约束,防止在分配后更改唯一ID,但是您必须在所有应用程序中执行相同操作...在我看来,在C#中使用它听起来比数据库更加维护,因为数据库已经内置了防止更改主键的方法。


4
2018-01-30 18:44



不确定为什么你被改装了。我认为您的代码中可能存在错误并不是不合理的。我认为有些人对这个建议感到不满。
嗯,对不起,如果它是这样的话,我是一个硬核C#'呃并且不打算这么说,我只是想说一下你在一个地方写的约束和数据库代码而忘了它,而不是它在几个C#应用程序中。 - Ricardo Villamil
我没有downmod,但我不认为第一句话是准确的。两个不同客户端生成的同一GUID的可能性非常低。 - Randolpho
我不认为里卡多的评论是关于在不同的C#调用中生成相同GUID的可能性,而是关于应用程序编码的变幻莫测,而不是将索引功能集中在数据库中。 - Joe Soul-bringer
不,但是有可能将2个不同的GUID分配给单行,这就是我读第一句话的方式。 - bart


有趣的问题。

传统上我也使用了DB分配的guid,但最近我正在使用Windows Mobile应用程序而SQL CE数据库不允许使用newguid所以我必须在代码中执行它。

我使用SQL复制将数据从移动设备获取到服务器。在过去的6个月里,我有40个SQL CE客户端将超过100000条记录同步回SQL 2005服务器而没有一个错过或重复的guid。

所需的额外编码可以忽略不计,插入前知道guid的好处实际上减少了一些复杂性。

我没有进行任何性能检查,所以性能除了我看不出任何理由不按照你的建议实现guid处理。


2
2018-01-30 21:07





GUID对于表现非常糟糕

我会把它留在数据库中,特别是现在SQL Server有 NEWSEQUENTIALID() 因为值是随机的,所以不会导致插入页面拆分,每个创建的NEWSEQUENTIALID都将大于前一个...只有caviat是它只能用作默认值


1
2018-01-30 18:45



我想,如果GUID是一个clusted索引,那么关于页面拆分的事情是正确的。 GUID可能是PK,但可能有另一个(更自然的)列更适合用作clusted索引。 - ChrisW
也许但默认情况下PK是聚集的,大多数人甚至没有意识到:-( - SQLMenace
看到 stackoverflow.com/questions/170346/... 用于在C#中创建顺序guid。 - Michael Meadows
GUID并非“表现糟糕” - 这是一种严重的夸大其词。在GUID上JOIN的SELECT查询将比在int上JOIN的类似查询慢(GUID连接的速度与CHAR或VARCHAR JOIN一样慢)。 GUID也占用了比int更多的空间,但这是微不足道的。 - MusiGenesis
“多一点空间”是一种严重的轻描淡写。


如果您不得不在GUI之外进行插入(考虑从其他供应商导入或从您购买的公司获取数据并且必须与您的数据合并),则不会自动分配GUID。这不是一个不可逾越的问题,但它仍然需要考虑。


1
2018-01-30 21:05





我让一个空的Guid指示这个对象虽然已构建,但尚未插入(或从中检索)数据库。


0
2018-01-30 18:46



我不同意这种方法。如何确定已更改属性的插入对象。即将删除的对象。 Guid.Empty不是这里最好的方法。 - Ray Booysen
必要时,我们在实体上使用单独的枚举字段,该实体是New / UnModified / Modified / Deleted之一,以明确跟踪数据库中的状态。 - AwesomeTown
@Ray aamof我做了一个选择以获取当前数据库中的内容:然后,将我所拥有的内容与我刚刚选择的内容进行比较,告诉我已编辑或删除的内容。 - ChrisW


正如SQLMenace所指出的那样,标准GUID会对索引和分页产生负面影响。在C#中,您可以使用一点P / Invoke乐趣生成顺序GUID,如NEWSEQUENTIALID()。

[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);

这样您至少可以继续使用GUID,但可以更灵活地生成它们的方式和位置。


0
2018-01-30 21:23



谢谢你的帖子!这太棒了... - bleepzter


好的,是时候进入了。我会说生成的GUID客户端用于保存到数据库是最好的方法 - 只要你碰巧使用GUID作为你的PK,我只推荐在一个场景中:断开连接环境。

当您使用断开连接的模型进行数据传播时(即PDA /手机应用程序,用于有限连接方案的笔记本电脑应用程序等),作为PK生成客户端的GUID是最好的方法。

对于其他所有情况,您可能最好使用自动增量标识PK。

为什么?好吧,有几个原因。首先,通过使用行跨越聚簇PK索引,您确实可以获得巨大的性能提升。 GUID PK和聚集索引不能很好地协同工作 - 即使使用NEWSEQUENTIALID,顺便说一下,我认为完全错过了GUID的重点。其次,除非您的情况迫使您不要(即您必须使用断开连接的模型),否则您确实希望保持所有事务处理并同时插入相同数量的相关数据。


0
2018-01-30 21:52





除了群集问题(如果正确设置索引,这似乎不是问题), 

作为索引的GUID总是非常混乱 - 没有“正确”的设置来避免这种情况(除非你在SQL Server引擎中使用NEWSEQUENTIALGUID函数)。

IMHO的最大缺点是大小 - GUID是16字节,INT是4.PK不仅存储在主键的树中,而且还存储在每个非聚集索引条目中。

有几千个条目,这可能没什么大不同 - 但如果你有一个包含数百万或数十亿条目和几个非聚集索引的表,使用16字节GUID而不是像PK那样的4字节INT所需空间的巨大差异 - 磁盘和RAM。

渣子


0
2018-01-31 09:29



但是,我的问题仍然是“我们是否应该使用GUID”(我们已经进行了讨论并使用了GUID)。相反,我很好奇在应用层而不是数据库层创建guid是否有任何缺点。 - AwesomeTown
是的,我明白了 - 但我觉得你错过了一个重要的观点,强烈反对使用GUID作为你的密钥。 - marc_s
如果 你真的必须坚持GUID,然后是的,在数据​​库端有一个大的PLUS创建它们 - 你可以使用SQL Server 2005“NEWSEQUENTIALGUID”功能,至少通过创建顺序(例如不断增加)来解决碎片的一些缺点)GUID。 - marc_s
如果你没有在你的PK上进行聚类,我仍然不清楚碎片问题的来源。据推测,你应该根据你将如何使用数据来聚类一个对聚类敏感的列。表。 - AwesomeTown
是的,如果您有非群集PK,那么使用GUID进行PK不是问题。但由于PK =群集密钥是默认设置并且被绝大多数人使用,因此通常是一个问题。 - marc_s