我刚刚阅读了一篇关于 NHibernate 从系统时间 (Guid.Comb) 创建 GUID 的能力的博客文章,从而避免了大量的数据库碎片。您可以将其称为与 SQL Server 顺序 ID 等效的客户端。
有没有办法可以在我的 Linq-to-Sql 项目中使用类似的策略(通过在代码中生成 Guid)?
我刚刚阅读了一篇关于 NHibernate 从系统时间 (Guid.Comb) 创建 GUID 的能力的博客文章,从而避免了大量的数据库碎片。您可以将其称为与 SQL Server 顺序 ID 等效的客户端。
有没有办法可以在我的 Linq-to-Sql 项目中使用类似的策略(通过在代码中生成 Guid)?
C#(安全)代码(NHibernate Guid Comb Generator 的赞美)
Guid GenerateComb()
{
byte[] destinationArray = Guid.NewGuid().ToByteArray();
DateTime time = new DateTime(0x76c, 1, 1);
DateTime now = DateTime.Now;
TimeSpan span = new TimeSpan(now.Ticks - time.Ticks);
TimeSpan timeOfDay = now.TimeOfDay;
byte[] bytes = BitConverter.GetBytes(span.Days);
byte[] array = BitConverter.GetBytes((long) (timeOfDay.TotalMilliseconds / 3.333333));
Array.Reverse(bytes);
Array.Reverse(array);
Array.Copy(bytes, bytes.Length - 2, destinationArray, destinationArray.Length - 6, 2);
Array.Copy(array, array.Length - 4, destinationArray, destinationArray.Length - 4, 4);
return new Guid(destinationArray);
}
github上的源链接: https ://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/GuidCombGenerator.cs
COMB 的生成方式如下:
DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)
转录成 C# 的内容如下所示:
public static unsafe Guid CombGuid()
{
Guid guid = Guid.NewGuid();
byte[] bytes = guid.ToByteArray();
long ticks = DateTime.Now.Ticks;
fixed( byte* pByte = bytes )
{
int* pFirst = (int *)(pByte + 10);
short* pNext = (short*)(pByte + 14);
*pFirst = (int)(ticks & 0xFFFFFF00);
*pNext = (short)ticks;
}
return new Guid( bytes );
}
好吧,您可以Guid
手动生成。但是, a 的优点之一Guid
是它不可猜测 - 即给定记录0000-...-0005
,通常没有意义(来自攻击者)检查记录0000-....-0004
等。
还有——重新碎片化?只要您对此数据有一个非聚集索引,我就不确定这是一个问题。您通常不会在 a 上放置聚集索引Guid
,因此该表将是一个堆(除非您有单独的聚集索引,例如IDENTITY
int)。在这种情况下,您将添加到末尾,并将新Guid
的插入到非聚集索引中。没有真正的痛苦。
(编辑)直接使用时间的一个问题是你会引入更多的碰撞风险;您需要担心紧密循环的Guid
创建(即在按顺序创建几个时避免重复),这意味着同步等 - 如果多台机器并行密集工作,它会变得更加麻烦 - 你很可能会得到重复.
你可以随时调用 UuidCreateSequential; 这是“旧”的 guid 生成器(2000 年以前,当 MSFT 将其更改为我们今天习惯的更随机的样式 guid 时)。他们将旧的 UuidCreate 重命名为 UuidCreateSequential,并将新的 guid 生成器放入 UuidCreate 的新实现中。UuidCreateSequential 也是 SQL Server 在 NewSequentialID() 中使用的,它与普通 guid 一样独特,但如果您在同一进程中连续创建一堆它们,它们的好处是它们是连续的。
using System;
using System.Runtime.InteropServices;
namespace System
{
public static class GuidEx
{
[DllImport("rpcrt4.dll", SetLastError = true)]
private static extern int UuidCreateSequential(out Guid guid);
private const int RPC_S_OK = 0;
/// <summary>
/// Generate a new sequential GUID. If UuidCreateSequential fails, it will fall back on standard random guids.
/// </summary>
/// <returns>A GUID</returns>
public static Guid NewSeqGuid()
{
Guid sequentialGuid;
int hResult = UuidCreateSequential(out sequentialGuid);
if (hResult == RPC_S_OK)
{
return sequentialGuid;
}
else
{
//couldn't create sequential guid, fall back on random guid
return Guid.NewGuid();
}
}
}
}
@arul,@Doug
为什么将时间部分放在 GUID 的末尾?
我认为前导字节对于排序更重要,而排序是首先引入时间部分以防止索引碎片的原因。
好的,我找到了答案,这个答案来自 Bernhard Kircher和他引用的网站Comparing GUID and uniqueidentifier Values (ADO.NET) 。
因此,以这种方式生成的 GUID 在 MS SQL-Server 以外的其他数据库上的工作方式不同,但这与 LINQ-to-SQL 无关。
对于变形的 URL 感到抱歉,但我没有足够的声誉来发布更多链接。
我们首先使用了与 Doug 在上面的实体框架模型中发布的方法类似的方法,因此您也必须能够使用 Linq to SQL 来完成。
While doing this we needed a comb guid generator for testing, and ended up building this little tool to generate comb guids online
http://www.webdesigncompany.co.uk/comb-guid/
Hopefully it will help you too.