我们使用Guid作为主键,默认情况下你知道它是聚类的.
将新行插入表格时,会将其插入表格中的随机页面(因为Guid是随机的).这具有可测量的性能影响,因为DB将始终分割数据页(碎片).但我的主要原因是顺序Guid是什么因为我希望将新行插入表中的最后一行...这将有助于调试.
我可以在CreateDate上创建聚簇索引,但我们的数据库是自动生成的,在开发中,我们需要做一些额外的事情来促进这一点.此外,CreateDate不适合聚簇索引.
在那天我使用了Jimmy Nielsons COMB,但我想知道.NET框架中是否存在某些内容.在SQL 2005中,Microsoft引入了newsequentialid()作为newid()的替代品,所以我希望他们创建一个.NET等价物,因为我们在代码中生成了ID.
PS:请不要开始讨论这是对还是错,因为GUID应该是唯一的等等.
应该可以使用对UuidCreateSequential的API调用在c#或vb.net中创建顺序GUID.下面的API声明(C#)取自Pinvoke.net,您还可以在其中找到如何调用该函数的完整示例.
[DllImport("rpcrt4.dll", SetLastError=true)] static extern int UuidCreateSequential(out Guid guid);
可以在此处找到与UuidCreateSequential函数相关的MSDN文章,其中包含使用的先决条件.
更新2018:还检查我的其他答案
这就是NHibernate生成sequantial ID的方式:
NHibernate.Id.GuidCombGenerator
////// Generate a new private Guid GenerateComb() { byte[] guidArray = Guid.NewGuid().ToByteArray(); DateTime baseDate = new DateTime(1900, 1, 1); DateTime now = DateTime.Now; // Get the days and milliseconds which will be used to build the byte string TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks); TimeSpan msecs = now.TimeOfDay; // Convert to a byte array // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 byte[] daysArray = BitConverter.GetBytes(days.Days); byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333)); // Reverse the bytes to match SQL Servers ordering Array.Reverse(daysArray); Array.Reverse(msecsArray); // Copy the bytes into the guid Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); return new Guid(guidArray); }using the comb algorithm. ///
确定添加行的顺序的一种简单方法可能是向表中添加IDENTITY列,从而无需保持GUID的顺序,从而避免在GUID列上维护聚簇索引的性能损失.
我不禁想知道在调试时如何保持这些行有助于你.你能扩展一下吗?
重要的是要注意,由SQL Server排序时,由UuidCreateSequential生成的UUID 不会是连续的。
SQL Server在排序UUID时遵循RFC
RFC弄错了
UuidCreateSequential
做对了
但是UuidCreateSequential
创建的内容与SQL Server期望的有所不同
背景
由UuidCreateSequential创建的Type 1 UUID在SQL Server中不排序。
SQL Server的NewSequentialID使用UuidCreateSequential,并应用了一些字节改组。从在线书籍:
NEWSEQUENTIALID(Transact-SQL)
NEWSEQUENTIALID是Windows UuidCreateSequential函数的包装,并应用了一些字节改组
然后引用MSDN博客文章:
如何在.NET中为SQL Server生成顺序GUID (存档)
public static Guid NewSequentialId() { Guid guid; UuidCreateSequential(out guid); var s = guid.ToByteArray(); var t = new byte[16]; t[3] = s[0]; t[2] = s[1]; t[1] = s[2]; t[0] = s[3]; t[5] = s[4]; t[4] = s[5]; t[7] = s[6]; t[6] = s[7]; t[8] = s[8]; t[9] = s[9]; t[10] = s[10]; t[11] = s[11]; t[12] = s[12]; t[13] = s[13]; t[14] = s[14]; t[15] = s[15]; return new Guid(t); }
这一切都始于自1582-10-15 00:00:00
1592年10月15日(公历改制为基督教历法的日期)以来的滴答数。刻度是100 ns间隔的数量。
例如:
2017年12月6日下午4:09:39 UTC
= 137,318,693,794,503,714滴答
= 0x01E7DA9FDCA45C22
滴答声
RFC表示,我们应该将此值分为三个部分:
UInt32低(4个字节)
Uint16中(2个字节)
UInt32 hi(2个字节)
因此,我们将其拆分:
0x01E7DA9FDCA45C22 | Hi | Mid | Low | |--------|--------|------------| | 0x01E7 | 0xDA9F | 0xDCA45C22 |
然后,RFC表示应按以下顺序写出这三个整数:
低: 0xDCA45C22
中: 0xDA9F
高: 0x01E7
如果遵循RFC,则这些值必须以big-endian(又称为“网络字节顺序”)书写:
DC A4 5C 22 DA 9F x1 E7 xx xx xx xx xx xx xx xx
这是一个糟糕的设计,因为您不能将UUID的前8个字节当作大端UInt64或小端UInt64对待。这是完全愚蠢的编码。
到目前为止,Microsoft遵循所有相同的规则:
低: 0xDCA45C22
中: 0xDA9F
高: 0x1E7
但他们以Intel 小端顺序将其写出:
22 5C A4 DC 9F DA E7 x1 xx xx xx xx xx xx xx xx
如果您看一下,您刚刚写了一个little-endian Int64
:
225CA4DC9FDAE701
含义:
如果您想提取时间戳
或按时间戳排序
这是微不足道的;只需将前8个字节视为UInt64。
使用RFC,您别无选择,只能执行各种位摆弄。即使在大端机器上,您也无法将64位时间戳视为64位时间戳。
给定一个来自小端的guid UuidCreateSequential
:
DCA45C22-DA9F-11E7-DDDD-FFFFFFFFFFFF
原始字节为:
22 5C A4 DC 9F DA E7 11 DD DD FF FF FF FF FF FF
这解码为:
Low Mid Version High -------- ---- ------- ---- ----------------- DCA45C22-DA9F-1 1E7 -DDDD-FFFFFFFFFFFF
低: 0xDCA45C22
中: 0xDA9F
高: 0x1E7
版本: 1(类型1)
我们可以按RFC big-endian顺序将其写回:
DC A4 5C 22 DA 9F 11 E7 DD DD FF FF FF FF FF FF
| Swap | Swap | Swap | Copy as-is Start index | 0 1 2 3 | 4 5 | 6 7 | End index | 3 2 1 0 | 5 4 | 7 6 | ---------------|-------------|-------|-------|------------------------ Little-endian: | 22 5C A4 DC | 9F DA | E7 11 | DD DD FF FF FF FF FF FF Big-endian: | DC A4 5C 22 | DA 9F | 11 E7 | DD DD FF FF FF FF FF FF