如何测试C#Sql server顺序GUID生成器?

时间:2012-06-27 06:32:50

标签: c# sql-server nhibernate guid sequence

关于如何创建Sql服务器索引友好的Guid,有许多方法,例如this教程。另一种流行的方法是来自NHibernate实现的方法(如下所列)。所以我认为编写一个实际测试此类代码的顺序要求的测试方法会很有趣。但是我失败了 - 我不知道什么是一个好的Sql服务器序列。我无法弄清楚他们是如何订购的。

例如,考虑到创建顺序guid的两种不同方式,如何确定哪种方式最好(速度除外)?例如,看起来两者都有缺点,如果它们的时钟被设置为2分钟(例如时间服务器更新),它们的序列会突然断开?但这是否也意味着Sql服务器索引的麻烦?

我使用此代码生成顺序Guid:

public static Guid CombFromArticle()
{
   var randomBytes = Guid.NewGuid().ToByteArray();
   byte[] timestampBytes = BitConverter.GetBytes(DateTime.Now.Ticks / 10000L);

   if (BitConverter.IsLittleEndian)
      Array.Reverse(timestampBytes);

   var guidBytes = new byte[16];

   Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10);
   Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);

   return new Guid(guidBytes);
}

public static Guid CombFromNHibernate()
{
  var destinationArray = Guid.NewGuid().ToByteArray();
  var time = new DateTime(0x76c, 1, 1);
  var now = DateTime.Now;
  var span = new TimeSpan(now.Ticks - time.Ticks);
  var timeOfDay = now.TimeOfDay;
  var bytes = BitConverter.GetBytes(span.Days);
  var 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);
}

文章中的那篇略快但是为SQL服务器创建了最佳序列?我可以填充100万条记录并比较碎片,但我甚至不确定如何正确验证。无论如何,我想了解如何编写一个测试用例,确保序列是Sql server定义的序列!

另外,我想对这两个实现提出一些意见。是什么让一个人比另一个更好?

1 个答案:

答案 0 :(得分:0)

我为SQL Server生成了顺序GUID。我以前从未看过太多文章..但是,似乎听起来很不错。

第一个,我使用系统函数生成(以获得正确的)和以下的函数,我只是递增。当然,你必须寻找溢出等(同样,GUID有几个字段)。

除此之外,没有什么难以考虑的。如果2个GUID是唯一的,那么它们的顺序也是如此,如果......你保持在几百万以下。嗯,这是数学..即使2个GUID也不能保证是独一无二的,至少从长远来看(如果人类不断增长)。因此,通过使用这种序列,您可能会将碰撞的概率从接近0增加到接近0(但略高)。如果有的话......请问一位数学家......这是生日问题http://en.wikipedia.org/wiki/Birthday_problem,有一个疯狂的天数。

它在C中,但应该可以轻松转换为更舒适的语言。特别是,您不必担心将wchar转换为char。

GUID guid;
bool bGuidInitialized = false;
void incrGUID()
{
    for (int i = 7; i >= 0; --i)
    {
        ++guid.Data4[i];
        if (guid.Data4[i] != 0)
            return;
    }
    ++guid.Data3;
    if (guid.Data3 != 0)
        return;
    ++guid.Data2;
    if (guid.Data2 != 0)
        return;
    ++guid.Data1;
    if (guid.Data1 != 0)
        return;
}

GenerateGUID(char *chGuid)
{
    if (!bGuidInitialized)
    {
        CoCreateGuid(&guid); 
        bGuidInitialized = true;
    }
    else
        incrGUID();

    WCHAR temp[42];
    StringFromGUID2(guid, temp, 42-1);
    wcstombs(chGuid, &(temp[1]), 42-1);
    chGuid[36] = 0;

    if (!onlyOnceLogGUIDAlreadyDone)
    {
        onlyOnceLogGUIDAlreadyDone = true;
        WR_cTools_LogTime(chGuid);
    }

    return ReturnCode;
}