当前位置:  开发笔记 > 编程语言 > 正文

是否有与SQL Server相同的.NET newsequentialid()

如何解决《是否有与SQLServer相同的.NETnewsequentialid()》经验,为你挑选了4个好方法。

我们使用Guid作为主键,默认情况下你知道它是聚类的.

将新行插入表格时,会将其插入表格中的随机页面(因为Guid是随机的).这具有可测量的性能影响,因为DB将始终分割数据页(碎片).但我的主要原因是顺序Guid是什么因为我希望将新行插入表中的最后一行...这将有助于调试.

我可以在CreateDate上创建聚簇索引,但我们的数据库是自动生成的,在开发中,我们需要做一些额外的事情来促进这一点.此外,CreateDate不适合聚簇索引.

在那天我使用了Jimmy Nielsons COMB,但我想知道.NET框架中是否存在某些内容.在SQL 2005中,Microsoft引入了newsequentialid()作为newid()的替代品,所以我希望他们创建一个.NET等价物,因为我们在代码中生成了ID.

PS:请不要开始讨论这是对还是错,因为GUID应该是唯一的等等.



1> John..:

应该可以使用对UuidCreateSequential的API调用在c#或vb.net中创建顺序GUID.下面的API声明(C#)取自Pinvoke.net,您还可以在其中找到如何调用该函数的完整示例.

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

可以在此处找到与UuidCreateSequential函数相关的MSDN文章,其中包含使用的先决条件.


我将此标记为正确答案.这不是我追求的.我想要一个COMB,我认为SequentialId和comb一样.那是......问题我的描述不一致.但它认为这是问题的正确答案.
请注意,此顺序guid在SQL Server中不是顺序的,因为它使用另一种排序算法,因此使用COMB是更好的选择

2> Gian Marco G..:

更新2018:还检查我的其他答案

这就是NHibernate生成sequantial ID的方式:

NHibernate.Id.GuidCombGenerator

/// 
/// Generate a new  using the comb algorithm.
/// 
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);
}



3> Ed Guiness..:

确定添加行的顺序的一种简单方法可能是向表中添加IDENTITY列,从而无需保持GUID的顺序,从而避免在GUID列上维护聚簇索引的性能损失.

我不禁想知道在调试时如何保持这些行有助于你.你能扩展一下吗?


处理此问题的标准方法是使用IDENTITY列.是的,它会为你的表添加一个额外的列,但这不应该是一个大问题.一行需要4到8个字节,具体取决于您的设置.它实际上是聚集索引的完美选择!

4> Ian Boyd..:

重要的是要注意,由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:001592年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对待。这是完全愚蠢的编码。

UuidCreateSequential正确处理

到目前为止,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


@MariuszJamro您希望较新的行实际存储在较旧的行之后。当较旧的数据在数据库中占据单独的页面时,可以将有限的RAM更有效地用于缓存当前使用的数据。另请参阅:[UUID“版本6”:RFC 4122版本忘记了](https://bradleypeabody.github.io/uuidv6/)
嗯,这一切都是关于SQL Server内部工作方式的。感谢您的澄清
推荐阅读
kikokikolove
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有