时间戳为8个字节,SQL Server中COMB GUID为时间戳为6个字节

时间:2014-03-06 20:33:14

标签: sql-server database guid

感谢精彩的文章The Cost of GUIDs as Primary Keys,我们有了COMB GUID。根据目前的实施情况,有两种方法:

  1. 使用最后6个字节作为时间戳:GUIDs as fast primary keys under multiple databases
  2. 使用windows tick:GUID COMB strategy in EF4.1 (CodeFirst)
  3. 将最后8个字节用于时间戳

    我们都知道,对于GUID的6字节时间戳,随机字节会有更多的字节来减少GUID的冲突。但是,将创建具有相同时间戳的更多GUID,并且这些GUID根本不是连续的。有了这个,8字节时间戳将是首选。

    所以这似乎是一个艰难的选择。根据上面GUIDs as fast primary keys under multiple databases的文章,它说:

      

    在我们继续之前,有一个关于这种方法的简短脚注:使用1毫秒分辨率的时间戳意味着非常靠近的GUID可能具有相同的时间戳值,因此不会是顺序的。这可能是某些应用程序的常见现象,实际上我尝试了一些替代方法,例如使用更高分辨率的计时器,如System.Diagnostics.Stopwatch,或将时间戳与“计数器”相结合,以保证序列一直持续到时间戳更新。但是,在测试过程中,我发现即使在同一个1毫秒的窗口内生成了数十甚至数百个GUID,这根本没有明显区别。这与Jimmy Nilsson在使用COMB进行测试时遇到的情况一致

    想知道是否有人知道数据库内部可以分享关于上述观察的一些灯光。是因为数据库服务器只是将数据存储在内存中,只有在达到某个阈值时才写入磁盘?因此,具有相同时间戳的非序列GUID的插入数据的重新排序通常会在内存中发生,因此性能损失最小。

    更新 根据我们的测试,与随机GUID相比,COMB GUID无法减少因互联网声称的表碎片。现在似乎唯一的方法是使用SQL Server生成顺序GUID。

1 个答案:

答案 0 :(得分:2)

您引用的文章来自2002年,而且非常古老。只需使用newsequentialid(在SQL Server 2005及更高版本中可用)。这可以保证您生成的每个新ID都大于前一个,从而解决索引碎片/页面拆分问题。

我想提到的另一个方面是,那篇文章的作者掩盖了,当你只需要4时使用16个字节并不是一个好主意。假设您有一个包含500,000行的表,平均150个字节,不包括聚簇列,并且该表有3个非聚簇索引(在每行中重复聚簇列),每个索引依次包含4个字节,25个字节和50个行字节不计算聚簇列。

然后是完美100%填充因子的存储要求(所有数字均以兆字节为单位,除非%):

Item  Clust  50     25     4      Total
----  -----  -----  -----  -----  ------
GUID  79.1   31.5   19.6    9.5   139.7
 int  73.4   25.7   13.8    3.8   116.7
%imp   7.2%  18.4%  29.6%  60.0%   16.5%

在非聚集索引中只有一个int列的4个字节(常见场景),将聚簇索引切换为int会使其缩小60%!对于桌面上的任何扫描,这直接转化为60%的性能提升 - 这是保守的,因为行数较少时,页面拆分的频率会降低,碎片会保持更长时间。

即使在聚集索引本身,仍然有7.2%的性能提升,这根本不是什么。

如果您在整个数据库中使用了GUID,该数据库具有与此类似的配置文件,那么切换到int将导致大小减少16.5%,数据库本身为1.397太字节大小?您的整个数据库将增加230 Gb(请参阅Total column,139.7 - 116.7)。这可以转化为现实世界中用于高可用性存储的真钱。它会提前及早移动您的磁盘购买计划,这对您公司的利润有害。

不要使用比必要更大的数据类型。这就像是无缘无故地为你的汽车增加重量:你支付它(如果没有速度,那么在燃油经济性方面)。

<强>更新

现在我知道您在客户端代码中创建了GUID,我可以更清楚地看到问题的本质。如果 能够推迟创建GUID直到行插入时间,这是实现此目的的一种方法。

首先,为CustomerID列设置默认值:

ALTER TABLE dbo.Customer ADD CONSTRAINT DF_Customer_CustomerID
   DEFAULT (newsequentialid()) FOR Customer;

现在您不必在任何CustomerID中指定要为INSERT插入的值,您的查询可能如下所示:

DECLARE @Name varchar(100) = 'Acme Spy Devices';
INSERT dbo.Customer (Name)
OUTPUT inserted.CustomerID -- a GUID
VALUES (@Name);

在这个非常简单的示例中,您已向Customer表中插入了一个新行,并在一个查询中将一个行集返回给包含刚刚创建的值的客户端。

如果您想明确插入可行的VALUES (newsequentialid(), @Name)