3

有很多关于如何创建对 Sql 服务器索引友好的 Guid 的方法,例如教程。另一种流行的方法是来自 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 定义的序列!

另外我想对这两个实现发表一些评论。是什么让一个比另一个更好?

4

1 回答 1

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;
}
于 2012-06-27T17:04:32.383 回答