20

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

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

4

6 回答 6

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

于 2010-02-02T21:37:18.953 回答
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 );
    }
于 2009-03-20T09:53:22.190 回答
3

好吧,您可以Guid手动生成。但是, a 的优点之一Guid是它不可猜测 - 即给定记录0000-...-0005,通常没有意义(来自攻击者)检查记录0000-....-0004等。

还有——重新碎片化?只要您对此数据有一个非聚集索引,我就不确定这是一个问题。您通常不会在 a 上放置聚集索引Guid,因此该表将是一个堆(除非您有单独的聚集索引,例如IDENTITYint)。在这种情况下,您将添加到末尾,并将新Guid的插入到非聚集索引中。没有真正的痛苦。

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

于 2009-03-20T09:40:10.817 回答
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();
            }
        }
    }
}
于 2010-02-03T06:10:08.213 回答
2

@arul,@Doug

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

我认为前导字节对于排序更重要,而排序是首先引入时间部分以防止索引碎片的原因。

好的,我找到了答案,这个答案来自 Bernhard Kircher和他引用的网站Comparing GUID and uniqueidentifier Values (ADO.NET) 。

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

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

于 2010-06-07T07:01:25.220 回答
0

我们首先使用了与 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.

于 2012-09-20T15:34:56.440 回答