Linq-to-Sql中的序列GUID?
我刚刚阅读了一篇关于NHibernate能够从系统时间(Guid.Comb)创build一个GUID的博客文章 ,从而避免了大量的数据库碎片。 您可以将其称为与SQL Server顺序ID等效的客户端。
有没有一种方法可以在我的Linq-to-Sql项目中使用类似的策略(通过在代码中生成Guid)?
COMBs按以下方式生成:
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 ); }
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
那么,你可以手动生成Guid
。 然而, Guid
的优点之一是不可猜测 – 即给定logging0000-...-0005
,检查logging0000-....-0004
等通常是很less的(来自攻击者) 。
另外 – 重新碎片? 只要你有这个数据非聚集索引,我不知道这是一个问题。 您通常不会在Guid
上放置聚簇索引,因此该表将成为堆(除非您有单独的聚簇索引,例如IDENTITY
int)。 在这种情况下,您将添加到最后,并将新的Guid
插入到非聚集索引中。 没有真正的痛苦。
(编辑)直接使用时间的一个问题是你引入了更多的碰撞风险, 你将需要担心Guid
创build的紧密循环(即避免重复创build几个序列时),这意味着同步等 – 而且如果多台机器并行工作,会变得更加麻烦 – 很可能会得到重复。
你总是可以调用UuidCreateSequential; 这是“旧”的guid生成器(在2000年之前,当MSFT将其更改为我们今天习惯使用的更随机样式的指导)之前。 他们将旧的UuidCreate重命名为UuidCreateSequential,并将其新的guid生成器放入UuidCreate的新实现中。 UuidCreateSequential也是SQL Server在NewSequentialID()中使用的,它和普通的GUID一样唯一,但是如果你在同一个进程中连续创build一堆,它们是连续的。
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的最后?
我认为前面的字节对于sorting来说更重要,sorting是为什么时间部分是为了防止索引碎片而引入的。
好的,我find了答案 ,这个答案来自Bernhard Kircher和比较他所引用的GUID和uniqueidentifier Values(ADO.NET)的网站。
这样生成的GUID在其他数据库上的工作方式与MS SQL-Server不同,但与LINQ-to-SQL无关。
抱歉变形的url,但我没有足够的声誉发布更多的链接。
我们使用了一个类似于Doug首先在Entity Framework模型中发布的方法,因此您必须能够使用Linq to SQL来完成此工作。
在这样做的时候,我们需要一个梳子引导发生器进行testing,并最终build立这个小工具来在线生成梳子引导
http://www.webdesigncompany.co.uk/comb-guid/
希望它也能帮助你。