【问题标题】:Sequential GUID in Linq-to-Sql?Linq-to-Sql 中的顺序 GUID?
【发布时间】:2009-03-20 09:07:28
【问题描述】:

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

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

【问题讨论】:

  • 我尝试了所有这些 COMB guid 样本,它们都产生了 10K 行的高碎片。 arul 的版本 - 98% 的碎片。
    NHibernate - 53% 的碎片。
    rpcrt4.dll - 98% 的碎片
    bigint - 6% 的碎片
    人们期望 COMB 商品的性能如何?
  • @Shaun 这取决于您插入的速度,如果您每秒只有几次插入,则应该没有碎片。

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


【解决方案1】:

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

【讨论】:

  • 刚刚被添加到我的项目中!
  • 很高兴听到这个消息。我在 NHibernate 源代码中找到了这个小宝石,只需要分享它:)
【解决方案2】:

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

【讨论】:

  • 这对我也很有帮助,但是我是否必须使用 /unsafe 编译我的整个程序,或者我可以将它放在它自己的类库中并使用 unsafe 标志编译它?跨度>
  • @Paladin:您可以在不使用不安全代码的情况下做到这一点。看看 BitConvert 类
  • 在下面查看我的答案以获得 Guid.Comb 生成的安全版本。
【解决方案3】:

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

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

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

【讨论】:

  • 我建议作为正确答案的解决方案将任意 Guid 与时间生成部分相结合,从而消除了重复的风险。我不知道它是否解决了任何碎片问题......
【解决方案4】:

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

【讨论】:

  • 仅供参考,如果您使用的是 Mono,那么 rpcrt4.dll 将不存在,这将不起作用。
  • 好点道格。我使用 UuidCreateSequential,但忘记了这个快照。
  • 机器重启了怎么办??那么你将失去顺序
  • @WahidBitar 用于碎片化,这无关紧要... ...除非您经常非常重新启动...
【解决方案5】:

@arul, @道格

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

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

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

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

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

【讨论】:

    【解决方案6】:

    我们首先使用了与 Doug 上面在 Entity Framework 模型中发布的方法类似的方法,因此您也必须能够使用 Linq to SQL 来做到这一点。

    在此过程中,我们需要一个用于测试的梳状 guid 生成器,并最终构建了这个用于在线生成梳状 guid 的小工具

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

    希望对你也有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多