【问题标题】:How to test C# Sql server sequential GUID generator?如何测试 C# Sql server 顺序 GUID 生成器?
【发布时间】:2012-06-28 01:04:32
【问题描述】:

有很多关于如何创建对 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 定义的序列!

我还想了解这两个实现的一些 cmets。是什么让一个比另一个更好?

【问题讨论】:

  • 你不是在过早优化吗?
  • 在 SQL Server 2012 中,序列能够做到这一点,并且很可能比您自己编写的任何东西都更有效
  • @jaypeagi 有趣的想法,但我猜他们为什么首先使用 GUID,以便它可以通过火灾生成应用程序并忘记插入。使用序列意味着必须从事实源 (SQL) 查询序列。如果序列可以工作,我很好奇为什么普通的身份列不工作(更快!)
  • 我想我写了你提到的那篇文章,我学到的最大的教训可能是保持顺序只在大局中很重要——小偏差并不会真正影响性能。如果您将时钟拨回两分钟,您可能会看到那段时间的性能有所下降,但话又说回来,您可能不会;这将取决于当前页面的填充方式以及必须重新洗牌的其他页面数量。它仍然比随机 GUID 好得多,一旦时钟“赶上”,序列将继续增加,一切都会恢复正常。

标签: c# sql-server nhibernate guid sequence


【解决方案1】:

我为 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    相关资源
    最近更新 更多