排序由UuidCreateSequential生成的顺序指南

时间:2016-07-22 21:59:50

标签: c# guid

我试图对由UuidCreateSequential生成的Guids进行排序,但是我看到结果不正确,我是不是在做什么?这是代码

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

EDIT1:

为了明白我的问题,为什么使用默认的比较器不能对guid结构进行排序?

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

的方式按升序排序
            "53cd98f2504a11e682838cdcd43024a7",
            "7178df9d504a11e682838cdcd43024a7",
            "800b5b69504a11e682838cdcd43024a7",
            "9796eb73504a11e682838cdcd43024a7",
            "c14c5778504a11e682838cdcd43024a7",
            "c14c5779504a11e682838cdcd43024a7",
            "d2324e9f504a11e682838cdcd43024a7",
            "d2324ea0504a11e682838cdcd43024a7",
            "da3d4460504a11e682838cdcd43024a7",
            "e149ff28504a11e682838cdcd43024a7",
            "f2309d56504a11e682838cdcd43024a7",
            "f2309d57504a11e682838cdcd43024a7",
            "fa901efd504a11e682838cdcd43024a7",
            "fa901efe504a11e682838cdcd43024a7",
            "036340af504b11e682838cdcd43024a7",
            "11768c0b504b11e682838cdcd43024a7",
            "2f57689d504b11e682838cdcd43024a7"

3 个答案:

答案 0 :(得分:5)

首先,让我们重新陈述观察结果:当创建具有巨大时间延迟的连续GUID - 60 十亿纳秒 - 创建之间时,生成的GUID不是连续的。

  我错过了什么吗?

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

您有一项服务,可以在Universe中的所有计算机上提供顺序唯一的数字。想一想这是怎么可能的。这不是一个神奇的盒子;有人必须写那段代码。

想象一下,如果您不必使用计算机,而是必须手动完成。您宣传服务:您向任何人提供顺序全球唯一号码随时

现在,假设我问你三个这样的数字然后分发20,21和22.然后 60年后我再问你三个并且惊喜,你给我13510985,13510986 “等一下这里”,我说,“我想要六个连续的数字,但是你给了我三个连续的数字,然后又给了我三个。什么给了?”

那么,你认为在那60年间会发生什么?请记住,您随时向任何要求的人提供此服务。在什么情况下你能给我23,24和25? 只有在60年内没有人问过

现在很明显为什么你的程序的行为完全符合它应该的样子?

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

现在,我告诫你,这只是一个起点;假设你有20台虚拟机都在同一台真机上,并且都试图同时生成顺序GUID?在这些情况下,碰撞变得更加可能。您可以考虑在这些场景中可能用于缓解冲突的技术。

答案 1 :(得分:1)

经过研究,我无法使用默认排序对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!");
}

答案 2 :(得分:0)

希望我能正确理解你的问题。您似乎正在尝试对Guids的HEX表示进行排序。这实际上意味着您要按字母顺序对其进行排序而不是数字

Guids将通过数据库中的字节值进行索引。这是一个控制台应用程序,用于证明您的Guids在数字上是连续的:

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