Linq-to-Sql中的顺序GUID?

时间:2009-03-20 09:07:29

标签: .net nhibernate linq-to-sql guid

我刚刚阅读了blog post关于NHibernate从系统时间(Guid.Comb)创建GUID的能力,从而避免了大量的数据库碎片。您可以将其称为客户端等效于SQL Server顺序ID。

有没有办法在Linq-to-Sql项目中使用类似的策略(通过在代码中生成Guid)?

6 个答案:

答案 0 :(得分:50)

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

答案 1 :(得分:9)

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 );
    }

答案 2 :(得分:3)

好吧,您可以手动生成Guid。然而,Guid的一个优点是它是不可猜测的 - 即给定记录0000-...-0005,通常很少(来自攻击者)检查记录0000-....-0004等。

另外 - 重新分裂?只要您对此数据有非聚集索引,我不确定这是一个问题。您通常不会在Guid上放置聚簇索引,因此该表将是一个堆(除非您有一个单独的聚簇索引,例如IDENTITY int)。在这种情况下,您将添加到结尾,并将新的Guid插入到非聚集索引中。没有真正的痛苦。

(编辑) 直接使用时间的一个问题是你引入了更多的碰撞风险;你需要担心紧密循环Guid创建(即在按顺序创建一些时避免重复),这意味着同步等等 - 如果多台机器并行工作,它会变得更加麻烦 - 很有可能你会得到重复的。

答案 3 :(得分:3)

你总是可以调用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();
            }
        }
    }
}

答案 4 :(得分:2)

@arul, @Doug

为什么你把时间部分放在GUID的末尾?

我认为前导字节对于排序来说更重要, 和排序是为什么首先引入时间部分以防止索引碎片。

好的,我找到了answer,这个answer from Bernhard Kircher以及他引用的网站Comparing GUID and uniqueidentifier Values (ADO.NET)

因此,以这种方式生成的GUID在MS SQL-Server之外的其他数据库上的工作方式不同,但这与LINQ-to-SQL无关。

对于已变形的网址感到抱歉,但我没有足够的声誉来发布更多链接。

答案 5 :(得分:0)

我们首先使用与Doug在Entity Framework模型中发布的方法类似的方法,因此您必须能够使用Linq to SQL来执行此操作。

在这样做的同时,我们需要一个梳子guid发生器进行测试,最终建立了这个小工具来在线生成梳子guids

http://www.webdesigncompany.co.uk/comb-guid/

希望它也能帮助你。