【问题标题】:What are the performance improvement of Sequential Guid over standard Guid? [closed]Sequential Guid 相对于标准 Guid 有哪些性能改进? [关闭]
【发布时间】:2010-09-15 06:46:12
【问题描述】:

有没有人测量过顺序 Guid 与标准 Guid 在数据库中用作主键时的性能?


我不认为需要唯一的密钥是可猜测的,从 Web UI 或其他部分传递它们本身似乎是一种不好的做法,如果您有安全问题,我不知道如何使用guid 可以改进一些事情(如果这是问题,请使用使用框架的适当加密功能的实随机数生成器)。
我的方法涵盖了其他项目,可以从代码生成顺序 guid,而无需访问数据库(也适用于 Windows),并且它在时间和空间上是独一无二的。
是的,提出问题的目的是为了回答它,为那些选择 Guid 进行 PK 的人们提供一种提高数据库使用率的方法(在我的例子中,它允许客户在无需更改服务器的情况下维持更高的工作量)。

似乎安全问题很多,在这种情况下不要使用顺序 Guid,或者更好的是,对 PK 使用标准 Guid,这些 Guid 从您的 UI 来回传递,而顺序 guid 用于其他所有内容。与往常一样,没有绝对的真理,我也编辑了主要答案以反映这一点。

【问题讨论】:

    标签: database primary-key guid


    【解决方案1】:

    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 服务器



    一种可能的解决方案是使用按如下方式生成的 Sequential Guid:

    cc6466f7-1066-11dd-acb6-005056c00008
    cc6466f8-1066-11dd-acb6-005056c00008
    cc6466f9-1066-11dd-acb6-005056c00008


    如何从 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 存储为 UniqueIdentifier SQL Server 上的类型
    • Guid 在 Oracle 上存储为 CHAR(36)
    • 大量插入操作,批处理 一起完成一次交易
    • 从 1 到 100 个刀片,具体取决于 在桌子上
    • 一些表 > 1000 万行



    实验室测试 - SQL Server

    VS2008 测试,10 个并发用户,无思考时间,600 次叶表批量插入的基准测试过程
    标准指南
    平均处理持续时间:10.5
    平均第二次请求:54.6
    平均响应。时间:0.26

    顺序引导
    平均处理持续时间:4.6
    平均第二次请求:87.1
    平均响应。时间:0.12

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

    标准 Guid, 0.02 秒。每次插入所用的时间,2.861 秒。 CPU 时间,总共 31.049 秒。过去

    顺序引导0.00 秒。每次插入所用的时间,1.142 秒。 CPU 时间,总共 3.667 秒。过去

    DB 文件顺序读取等待时间从 62.415 秒的 6.4 百万个等待事件变为 1.2 百万个等待事件11.063 秒。

    重要的是要看到所有的顺序 guid 都可以猜到,所以如果安全是一个问题,使用它们不是一个好主意,仍然使用标准 guid。
    简而言之...如果您将 Guid 用作 PK,则每次不从 UI 来回传递它们时都使用顺序 guid,它们将加快操作并且不需要任何成本来实现。

    【讨论】:

    • 使用存储引擎“InnoDB”,MySQL 以集群方式通过 PK 存储记录,因此您也应该从 Sequential GUID 中受益。
    • “重要的是看到所有的顺序 guid 都可以被猜到,所以如果安全是一个问题,使用它们不是一个好主意”在这种情况下,可以使用 Comb guid,它具有顺序和随机的好处。
    • 请参阅此博客文章:blogs.msdn.com/b/dbrowne/archive/2012/07/03/…"... UuidCreateSequential 的结果相对于 SQL Server 的排序顺序不是连续的...为了使它们连续,SQL Server 的内部 NEWSEQUENTIALID 函数对GUID...您需要执行相同的字节洗牌”
    • 为什么更好是我不明白的。
    • 使用顺序 guid 而不是顺序整数的目的是什么?
    【解决方案2】:

    我可能在这里遗漏了一些东西(如果我有,请随时纠正我),但我发现将顺序 GUID/UUID 用于主键几乎没有什么好处。

    在自动递增整数上使用 GUID 或 UUID 的要点是:

    • 可以在任何地方创建它们无需联系数据库
    • 它们是在您的应用程序中完全唯一的标识符(在 UUID 的情况下,是普遍唯一的)
    • 给定一个标识符,除了暴力破解 巨大的键空间之外,无法猜测下一个或前一个(甚至任何其他有效标识符)。李>

    不幸的是,根据你的建议,你失去了所有这些东西。

    所以,是的。您使 GUID 变得更好。但在这个过程中,你几乎一开始就抛弃了使用它们的所有理由。

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

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

    【讨论】:

    • 除了“非猜测”(我认为不重要,我们不希望随机化函数)之外,顺序 guid 具有您正在寻找的特性,我从 C# 生成它们代码,它们在时间和空间上都是唯一的。
    • 顺序 UUID 不保证全局排序。它们仍然是普遍唯一的,但它们也是局部顺序的。这意味着在不同主机/进程/线程(取决于顺序方案)上生成的 ID 随机交错,但在同一环境中生成的 ID 将被排序。
    • COMB GUID 是有序的,插入/读取速度非常快,并且提供与标识列相当的速度。标识列的所有性能,但您不需要使用任何带有 GUID 的疯狂复制策略。你做的身份列。优势 GUID。
    • 如果在云端,标准的自增整数主键从长远来看并不好。
    • 跨表不是唯一的。云适用于网络规模。除非您的数据库非常小,否则没关系。
    【解决方案3】:

    查看 Jimmy Nilsson 的 COMBs:一种 GUID,其中许多位已替换为类似时间戳的值。这意味着 COMB 可以被排序,并且当用作主键时,在插入新值时会减少索引页面拆分。

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

    【讨论】:

    • 我对 COMB 和类似技术有点怀疑,因为“GUID 是全局唯一的,但 GUID 的子字符串不是”:blogs.msdn.com/oldnewthing/archive/2008/06/27/8659071.aspx
    • GUID 在统计上是唯一的。也就是说,碰撞的机会非常小。 COMB 牺牲了 GUID 中可用的 128 位中的一些位。所以是的,碰撞的可能性更高,但仍然非常低。
    • guid 的全部意义在于它们具有比整数更高的全局唯一性概率。这个概率不一定是 100%。虽然使用 COMB guid 会增加发生冲突的概率,但它仍然比使用标识列低许多数量级。
    【解决方案4】:

    如果您需要使用顺序 GUId,SQL Server 2005 可以使用 NEWSEQUENTIALID() 函数为您生成它们。

    然而由于 GUId 的基本用法是生成无法猜测的密钥(或备用密钥)(例如,避免人们在 GET 上传递猜测的密钥),我看不出有多适用它们是因为它们很容易被猜到。

    来自MSDN

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

    【讨论】:

    • 我再说一遍,我没有看到 Guid 用于生成无法猜测的密钥,而是作为一种拥有在时间和空间上唯一且可以轻松用于复制的密钥的方法,如果隐私很重要的话使用其他方法(真实随机数)
    【解决方案5】:

    正如 massimogentilini 所说,使用 UuidCreateSequential(在代码中生成 guid 时)可以提高性能。但是似乎缺少一个事实:SQL Server(至少 Microsoft SQL 2005 / 2008)使用相同的功能,但是:Guid 的比较/排序在 .NET 和 SQL Server 上不同,这仍然会导致更多 IO,因为不会正确订购指南。 为了生成为 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 linkthis link

    【讨论】:

    【解决方案6】:

    见这篇文章: (http://www.shirmanov.com/2010/05/generating-newsequentialid-compatible.html)

    即使 MSSql 使用相同的函数来生成 NewSequencialIds ( UuidCreateSequential(out Guid guid) ),MSSQL 反转第 3 和第 4 字节模式,这不会给您在代码中使用此函数时获得的相同结果。 Shirmanov 展示了如何获得与 MSSQL 完全相同的结果。

    【讨论】:

      【解决方案7】:

      好的,我自己的设计和制作终于走到了这一步。

      我生成了一个 COMB_GUID,其中高 32 位基于 Unix 时间的第 33 位到第 1 位,以毫秒为单位。因此,每 2 毫秒有 93 位随机性,高位的翻转每 106 年发生一次。 COMB_GUID(或 4 类 UUID)的实际物理表示是 128 位的 base64 编码版本,即 22 个字符的字符串。

      在 postgres 中插入时,完全随机的 UUID 和 COMB_GUID 之间的速度比对 COMB_GUID 有利。 在我的硬件上,COMB_GUID 的速度比我的硬件快 2X,超过了 100 万条记录测试。记录包含 id(22 个字符)、一个字符串字段(110 个字符)、一个双精度和一个 INT。

      在 ElasticSearch 中,两者在索引方面没有明显区别。我仍将使用 COMB_GUIDS 以防内容进入链中任何位置的 BTREE 索引,因为内容与时间相关,或者可以在 id 字段上进行预排序,使其IS与时间相关且部分顺序,它会加快速度。

      很有趣。 制作 COMB_GUID 的 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】:

        我使用实体框架混淆了 Guid(集群和非集群)、顺序 Guid 和 int(身份/自动增量)之间的区别。与具有身份的 int 相比,Sequential Guid 的速度惊人地快。 Results and code of the Sequential Guid here.

        【讨论】:

        • 未找到结果。我会对你如何测量差异感兴趣。经常使用的标准 guid 的问题是插入时的页面拆分,这会慢慢导致查询性能下降。您是否以导致页面拆分的方式进行插入?
        • 网址已更新,您可以看到结果。
        • 谢谢。一个非常有趣的分析。做这样的事情会很棒,但是然后查询每个表的碎片程度。然后将高度碎片化的 Guid 表上的查询与非唯一的 int 表进行比较。我目前正在将 Guid 切换为 COMB Guid,希望这能提高查询性能。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多