【问题标题】:Sort Sequential Guids Generated by UuidCreateSequential排序由 UuidCreateSequential 生成的顺序 Guid
【发布时间】:2016-07-22 21:59:50
【问题描述】:

我尝试对 UuidCreateSequential 生成的 Guid 进行排序,但我看到结果不正确,是我遗漏了什么吗?这是代码

    private class NativeMethods
    {
        [DllImport("rpcrt4.dll", SetLastError = true)]
        public static extern int UuidCreateSequential(out Guid guid);
    }

    public static Guid CreateSequentialGuid()
    {
        const int RPC_S_OK = 0;

        Guid guid;
        int result = NativeMethods.UuidCreateSequential(out guid);
        if (result == RPC_S_OK)
            return guid;
        else throw new Exception("could not generate unique sequential guid");
    }

    static void TestSortedSequentialGuid(int length)
    {
        Guid []guids = new Guid[length];
        int[] ids = new int[length];

        for (int i = 0; i < length; i++)
        {
            guids[i] = CreateSequentialGuid();
            ids[i] = i;
            Thread.Sleep(60000);
        }

        Array.Sort(guids, ids);

        for (int i = 0; i < length - 1; i++)
        {
            if (ids[i] > ids[i + 1])
            {
                Console.WriteLine("sorting using guids failed!");
                return;
            }
        }

        Console.WriteLine("sorting using guids succeeded!");
    }

编辑1:

为了澄清我的问题,为什么 guid 结构不能使用默认比较器排序?

编辑 2: 这里还有一些我生成的顺序 guid,似乎它们没有按照十六进制字符串的升序排序

            "53cd98f2504a11e682838cdcd43024a7",
            "7178df9d504a11e682838cdcd43024a7",
            "800b5b69504a11e682838cdcd43024a7",
            "9796eb73504a11e682838cdcd43024a7",
            "c14c5778504a11e682838cdcd43024a7",
            "c14c5779504a11e682838cdcd43024a7",
            "d2324e9f504a11e682838cdcd43024a7",
            "d2324ea0504a11e682838cdcd43024a7",
            "da3d4460504a11e682838cdcd43024a7",
            "e149ff28504a11e682838cdcd43024a7",
            "f2309d56504a11e682838cdcd43024a7",
            "f2309d57504a11e682838cdcd43024a7",
            "fa901efd504a11e682838cdcd43024a7",
            "fa901efe504a11e682838cdcd43024a7",
            "036340af504b11e682838cdcd43024a7",
            "11768c0b504b11e682838cdcd43024a7",
            "2f57689d504b11e682838cdcd43024a7"

【问题讨论】:

  • 运行您的精确样本,我能够在 x86 上生成顺序 guid。无法重现。请详细介绍系统设置
  • 每次迭代都需要长时间睡眠,睡眠后,它们会被排序
  • 不...取消你的睡眠。一切都还好。结果见here
  • 对不起,我的意思是 sleep 它不起作用
  • 有什么特殊的原因需要等待一分钟来生成一个 guid?我真的不愿意等待 10 分钟来重现它。但我认为,它不能以这种方式工作,因为序列可能会在这么长时间的休息时被打断

标签: c# guid


【解决方案1】:

首先,让我们重申一下观察结果:当创建具有巨大时间延迟(60 十亿纳秒)的顺序 GUID 时,生成的 GUID 不是顺序的。

我错过了什么吗?

你知道你需要知道的每一个事实来弄清楚发生了什么。你只是没有把它们放在一起。

您有一项服务,它提供顺序在宇宙中所有计算机上唯一的数字。想一想这是怎么可能的。这不是魔法盒;必须有人编写该代码。

想象一下,如果您不必使用计算机进行操作,而是必须手动操作。您宣传一项服务:您向随时询问的任何人提供连续的全球唯一编号

现在,假设我向你要三个这样的数字,你给出 20、21 和 22。然后 60 年后我又要你三个,你给我 13510985、13510986和 13510987。“在这里等一下”,我说,“我想要六个序列号,但你给了我三个序列号,然后又给了三个序列号。什么给了?”

那么,您认为在这 60 年间发生了什么?请记住,您可以随时向任何提出要求的人提供此服务。在什么情况下可以给我 23、24 和 25? 只有在这 60 年内没有其他人提出要求时

现在是否清楚为什么您的程序的行为完全符合其应有的行为?

实际上,顺序 GUID 生成器使用当前时间作为其策略的一部分来强制执行全局唯一属性。当前时间和当前位置是创建唯一编号的合理起点,因为在任何时候您的办公桌上都可能只有一台计算机。

现在,我提醒您,这只是一个起点;假设您有 20 个虚拟机都在同一台真实机器上,并且都试图同时生成顺序 GUID?在这些情况下,碰撞变得更有可能。您可能会想到在这些情况下可以用来减轻冲突的技术。

【讨论】:

  • 据我了解它使用MAC地址来保证唯一性和时间来保证顺序性,
  • “你有一项服务,它提供的数字在宇宙中的所有计算机上都是连续的和唯一的……这不是一个魔术盒;必须有人编写那个代码。” - 我以为有一个demon 生成了所有的数字!
  • 问题是关于排序的 guid 而不是顺序模式。所以生成的 guid 不会使用默认的 guid 排序
【解决方案2】:

经过研究,我无法使用默认排序对 guid 进行排序,甚至无法使用 guid.ToString 中的默认字符串表示形式,因为字节顺序不同。

要对 UuidCreateSequential 生成的 guid 进行排序,我需要转换为 BigInteger 或形成我自己的字符串表示形式(即十六进制字符串 32 个字符),方法是将具有最高意义的字节按最低有效顺序排列,如下所示:

static void TestSortedSequentialGuid(int length)
{
    Guid []guids = new Guid[length];
    int[] ids = new int[length];

    for (int i = 0; i < length; i++)
    {
        guids[i] = CreateSequentialGuid();
        ids[i] = i;

// this simulates the delay between guids creation
// yes the guids will not be sequential as it interrupts generator 
// (as it used the time internally) 
// but still the guids should be in increasing order and hence they are     
// sortable and that was the goal of the question
        Thread.Sleep(60000);
    }

        var sortedGuidStrings = guids.Select(x =>
        {
            var bytes = x.ToByteArray();

          //reverse high bytes that represents the sequential part (time)            
            string high = BitConverter.ToString(bytes.Take(10).Reverse().ToArray());

             //set last 6 bytes are just the node (MAC address) take it as it is.
                return high + BitConverter.ToString(bytes.Skip(10).ToArray());
        }).ToArray();

    // sort ids using the generated sortedGuidStrings
    Array.Sort(sortedGuidStrings, ids);

    for (int i = 0; i < length - 1; i++)
    {
        if (ids[i] > ids[i + 1])
        {
            Console.WriteLine("sorting using sortedGuidStrings failed!");
            return;
        }
    }

    Console.WriteLine("sorting using sortedGuidStrings succeeded!");
}

【讨论】:

    【解决方案3】:

    希望我正确理解了您的问题。您似乎正在尝试对您的 Guid 的十六进制表示进行排序。这实际上意味着您是按字母顺序而不是数字对它们进行排序。

    Guid 将根据它们在数据库中的字节值进行索引。这是一个控制台应用程序,用于证明您的 Guid 是数字顺序的:

    using System;
    using System.Linq;
    using System.Numerics;
    
    class Program
    {
        static void Main(string[] args)
        {
            //These are the sequential guids you provided.
            Guid[] guids = new[]
            {
                "53cd98f2504a11e682838cdcd43024a7",
                "7178df9d504a11e682838cdcd43024a7",
                "800b5b69504a11e682838cdcd43024a7",
                "9796eb73504a11e682838cdcd43024a7",
                "c14c5778504a11e682838cdcd43024a7",
                "c14c5779504a11e682838cdcd43024a7",
                "d2324e9f504a11e682838cdcd43024a7",
                "d2324ea0504a11e682838cdcd43024a7",
                "da3d4460504a11e682838cdcd43024a7",
                "e149ff28504a11e682838cdcd43024a7",
                "f2309d56504a11e682838cdcd43024a7",
                "f2309d57504a11e682838cdcd43024a7",
                "fa901efd504a11e682838cdcd43024a7",
                "fa901efe504a11e682838cdcd43024a7",
                "036340af504b11e682838cdcd43024a7",
                "11768c0b504b11e682838cdcd43024a7",
                "2f57689d504b11e682838cdcd43024a7"
            }.Select(l => Guid.Parse(l)).ToArray();
    
            //Convert to BigIntegers to get their numeric value from the Guids bytes then sort them.
            BigInteger[] values = guids.Select(l => new BigInteger(l.ToByteArray())).OrderBy(l => l).ToArray();
    
            for (int i = 0; i < guids.Length; i++)
            {
                //Convert back to a guid.
                Guid sortedGuid = new Guid(values[i].ToByteArray());
    
                //Compare the guids. The guids array should be sequential.
                if(!sortedGuid.Equals(guids[i]))
                    throw new Exception("Not sequential!");
            }
    
            Console.WriteLine("All good!");
            Console.ReadKey();
        }
    }
    

    【讨论】:

    • 所以我无法使用默认比较器对 guid 进行排序? UUIDCreateSequential 返回顺序 guid,我想我可以使用默认的 guid 比较器对它们进行排序
    猜你喜欢
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    相关资源
    最近更新 更多