代码之家  ›  专栏  ›  技术社区  ›  massimogentilini

顺序guid比标准guid的性能有什么改进?

  •  63
  • massimogentilini  · 技术社区  · 16 年前

    有人曾经测量过作为数据库中主键的顺序guid和标准guid的性能吗?

    9 回复  |  直到 11 年前
        1
  •  100
  •   Community Egal    7 年前

    guid与顺序guid



    一个典型的模式,它使用guid作为表的pk,但正如其他讨论中所提到的(请参见 Advantages and disadvantages of GUID / UUID database keys ) 有一些性能问题。

    这是一个典型的guid序列

    F3818D69-2552-40B7-A403-01A6DB4552F7
    7CE31615-FAFB-42C4-B317-40D21A6A3C60
    94732FC7-768E-4CF2-9107-F0953F6795A5


    此类数据的问题有:<
    -

    • 广泛的价值分布
    • 几乎是随机的
    • 索引使用非常非常非常糟糕
    • 很多树叶在动
    • 几乎每个pk都至少需要 在非聚集索引上
    • Oracle和 SQL Server



    一个可能的解决方案是使用顺序的guid,其生成方式如下:

    CC6466F7-1066-11DD-ACB6-00505660008
    CC6466F8-1066-11DD-ACB6-00505660008
    CC6466F9-1066-11DD-ACB6-00505660008


    如何从C代码生成它们:

    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out Guid guid);
    
    public static Guid SequentialGuid()
    {
        const int RPC_S_OK = 0;
        Guid g;
        if (UuidCreateSequential(out g) != RPC_S_OK)
            return Guid.NewGuid();
        else
            return g;
    }
    


    效益

    • 更好地使用索引
    • 允许使用群集密钥(将 在NLB方案中验证)
    • 磁盘使用较少
    • 性能提高的20-25% 最小成本



    实际测量: 脚本:

    • 存储为唯一标识符的GUID SQL Server上的类型
    • 在Oracle上存储为char(36)的guid
    • 批量插入操作 在一次交易中
    • 从1到100秒的插入取决于 在桌子上
    • 某些表的行数超过1000万



    实验室测试“SQL服务器”

    VS2008测试,10个并发用户,无思考时间,为叶表批量600个插入的基准进程
    标准GUID
    平均进程持续时间: 十点五
    每秒平均请求数: 五十四点六
    AVG公司时间: 零点二六

    序贯GUID
    平均进程持续时间: 四点六
    每秒平均请求数: 八十七点一
    AVG公司时间: 零点一二

    Oracle上的结果 (抱歉,用于测试的工具不同)1.327.613在带有guid pk的表上插入

    标准GUID , 零点零二 秒。每次插入所用的时间, 二点八六一 秒。CPU时间,总计 三十一点零四九 秒。逝去

    序贯GUID , 秒。每次插入所用的时间, 一点一四二 秒。CPU时间,总计 三点六六七 秒。逝去

    数据库文件顺序读取等待时间从 六点四 数百万人等待事件 六十二点四一五 秒到 一点二 百万等待事件 十一点零六三 秒。

    重要的是要确保所有顺序的guid都能被猜测出来,所以如果安全性是一个问题,那么最好还是使用标准的guid来使用它们。
    简而言之…如果您使用guid作为pk,那么每次它们没有从用户界面来回传递时都使用顺序guid,那么它们将加快操作速度,并且不需要花费任何代价来实现。

        2
  •  47
  •   Dan Aditi    16 年前

    我可能在这里遗漏了一些东西(如果是的话,请随时纠正我),但是对于主键使用顺序的guid/uuid,我看不到什么好处。

    这个 指向 在自动递增整数上使用guid或uuid的方法是:

    • 它们可以在任何地方创建 没有 正在联系数据库
    • 它们是应用程序中完全唯一的标识符(对于UUID,也是普遍唯一的标识符)
    • 给定一个标识符,就无法猜测下一个或上一个(甚至 任何 其他有效标识符)在强制 巨大的 密钥空间。

    不幸的是,利用你的建议,你输了 全部的 那些东西。

    所以,是的。你把吉他做得更好了。但是在这个过程中,你已经把使用它们的所有原因都抛弃了。

    如果你 真的? 要提高性能,请使用标准的自动递增整数主键。这提供了您描述的所有好处(以及更多),同时在几乎所有方面都优于“顺序GUID”。

    这很可能会被遗忘,因为它没有具体回答你的问题(显然是精心设计的,这样你就可以自己立即回答),但我觉得这是一个更重要的一点提出。

        3
  •  19
  •   nawfal Donny V.    11 年前

    正如Massimogentini已经说过的,使用UuidCreateSequence(在代码中生成guid时)可以提高性能。但似乎缺少一个事实:SQL Server(至少是Microsoft SQL 2005/2008)使用了相同的功能,但是:guid的比较/排序在.NET和SQL Server上有所不同,这仍然会导致更多的IO,因为guid的排序不正确。 为了为SQL Server生成正确排序的guid(排序),必须执行以下操作(请参见 comparison 细节):

    [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(byte[] buffer);
    
    static Guid NewSequentialGuid() {
    
        byte[] raw = new byte[16];
        if (UuidCreateSequential(raw) != 0)
            throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
    
        byte[] fix = new byte[16];
    
        // reverse 0..3
        fix[0x0] = raw[0x3];
        fix[0x1] = raw[0x2];
        fix[0x2] = raw[0x1];
        fix[0x3] = raw[0x0];
    
        // reverse 4 & 5
        fix[0x4] = raw[0x5];
        fix[0x5] = raw[0x4];
    
        // reverse 6 & 7
        fix[0x6] = raw[0x7];
        fix[0x7] = raw[0x6];
    
        // all other are unchanged
        fix[0x8] = raw[0x8];
        fix[0x9] = raw[0x9];
        fix[0xA] = raw[0xA];
        fix[0xB] = raw[0xB];
        fix[0xC] = raw[0xC];
        fix[0xD] = raw[0xD];
        fix[0xE] = raw[0xE];
        fix[0xF] = raw[0xF];
    
        return new Guid(fix);
    }
    

    this link this link .

        4
  •  4
  •   Sklivvz    16 年前

    如果你 需要 要使用顺序guid,SQL Server 2005可以使用 NEWSEQUENTIALID() 功能。

    然而 因为guid的基本用法是生成无法猜测的键(或备用键)(例如,为了避免人们在get上传递猜测的键),所以我看不出它们有多适用,因为它们很容易被猜测到。

    MSDN :

    重要:
    如果担心隐私问题,请不要使用此功能。它 可以猜测 下一个生成的guid,因此, 访问与该GUID关联的数据。

        5
  •  4
  •   massimogentilini    13 年前

    请参阅本文: ( http://www.shirmanov.com/2010/05/generating-newsequentialid-compatible.html )

    尽管mssql使用相同的函数生成newsequencealids (uuidcreateSequential(out-guid-guid)),mssql反转第3和第4字节模式,这与在代码中使用此函数时得到的结果不同。Shirmanov演示了如何获得与MSSQL创建的结果完全相同的结果。

        6
  •  3
  •   Mitch Wheat Scott Wisniewski    11 年前

    退房 COMBs 作者:JimmyNilsson:一种guid类型,其中许多位被类似时间戳的值替换。这意味着可以对组合进行排序,当用作主键时,插入新值时,索引页拆分会减少。

    Is it OK to use a uniqueidentifier (GUID) as a Primary Key?

        7
  •  2
  •   Dennis    11 年前

    好吧,我自己在设计和生产中终于达到了这一点。

    我生成一个梳状guid,其中32位的上限是基于UNIX时间的33到1位(以毫秒为单位)。因此,每2毫秒就有93个随机位,每106年就会发生一次高位翻转。comb-guid(或类型4-uuid)的实际物理表示是128位的base64编码版本,它是一个22个字符的字符串。

    在Postgres中插入时,完全随机UUID和梳状guid之间的速度比对梳状guid有利。 组合GUID是 2X 在我的硬件上比多次测试更快,达到了一百万次记录测试。记录包含id(22个字符)、字符串字段(110个字符)、双精度和int。

    在ElasticSearch中,两种索引方法之间没有明显的区别。我仍然会使用梳状guids,以防内容进入链中任意位置的btree索引,因为内容与fed时间相关,或者可以在id字段上预先排序,以便 时间相关,部分顺序,它将加快。

    很有趣。 下面是制作COMBUGID的Java代码。

    import java.util.Arrays;
    import java.util.UUID;
    import java.util.Base64; //Only avail in Java 8+
    import java.util.Date;
    
    import java.nio.ByteBuffer; 
    
        private ByteBuffer babuffer = ByteBuffer.allocate( (Long.SIZE/8)*2 );
    private Base64.Encoder encoder = Base64.getUrlEncoder();
    public  String createId() {
        UUID uuid = java.util.UUID.randomUUID();
            return uuid2base64( uuid );
    }
    
        public String uuid2base64(UUID uuid){ 
    
            Date date= new Date();
            int intFor32bits;
            synchronized(this){
            babuffer.putLong(0,uuid.getLeastSignificantBits() );
            babuffer.putLong(8,uuid.getMostSignificantBits() );
    
                    long time=date.getTime();
            time=time >> 1; // makes it every 2 milliseconds
                    intFor32bits = (int) time; // rolls over every 106 yers + 1 month from epoch
                    babuffer.putInt( 0, intFor32bits);
    
        }
            //does this cause a memory leak?
            return encoder.encodeToString( babuffer.array() );
        }
    

    }

        8
  •  2
  •   Alex Siepman    11 年前

    我使用实体框架测量了guid(集群和非集群)、顺序guid和int(标识/自动增量)之间的差异。与具有标识的int相比,序列guid的速度惊人。 Results and code of the Sequential Guid here .

        9
  •  1
  •   massimogentilini    16 年前

    我不认为需要唯一的密钥是可猜测的或不可猜测的,从Web用户界面或其他部分传递它们本身就是一个糟糕的实践,我也不认为,如果您有安全问题,如何使用guid可以改善事情(如果这是问题,使用真正的随机数生成器使用框架的适当加密函数)。
    我的方法涵盖了其他项目,可以从代码中生成顺序的guid,而不需要数据库访问(如果仅限于Windows),而且它在时间和空间上是唯一的。
    是的,提出这个问题的目的是为了回答这个问题,给那些为他们的pk选择guid的人一种提高数据库使用率的方法(在我的例子中,允许客户在不需要更换服务器的情况下维持更高的工作负载)。

    看起来安全性问题很多,在这种情况下,不要使用顺序的guid,或者更好的是,使用标准的guid来处理从您的用户界面来回传递的pk,而顺序的guid来处理其他一切。因为一直没有绝对的真理,我也编辑了主要的答案来反映这一点。