Sequential Guid相对于标准Guid的性能提升有哪些?

时间:2008-10-04 13:43:40

标签: database primary-key guid

在数据库中用作主键时,有人曾测量过Sequential Guid与Standard Guid的性能吗?

9 个答案:

答案 0 :(得分:102)

GUID vs.Secutivetial 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



一种可能的解决方案是使用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
魅力。 RESP。时间: 0.26

顺序导航
魅力。处理持续时间: 4.6
魅力。请求第二个: 87.1
魅力。 RESP。时间: 0.12

Oracle上的结果(抱歉,用于测试的不同工具)1.327.613在带有Guid PK的桌面上插入
标准指南 0.02 秒。每次插入的经过时间 2.861 秒。 CPU时间,总计 31.049 秒。过去

顺序指导 0.00 秒。每次插入的经过时间, 1.142 秒。 CPU时间,总计 3.667 秒。过去

数据库文件顺序读取等待时间从 6.4 数百万等待事件 62.415 秒传递到 1.2 百万等待事件 11.063 秒。

重要的是要看到所有顺序guid都可以被猜到,所以如果安全性是一个问题,使用它们并不是一个好主意,仍然使用标准guid。
为了缩短它...如果你使用Guid作为PK,每次它们不会从UI传回来时使用顺序guid,它们将加快操作并且不需要花费任何费用来实现。

答案 1 :(得分:50)

我可能在这里遗漏了一些东西(如果我有的话,请随时纠正我),但是我可以看到使用顺序GUID / UUID作为主键的好处很少。

使用GUID或UUID而不是自动增量整数的是:

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

不幸的是,使用你的建议,你会丢失所有那些东西。

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

如果确实想要提高性能,请使用标准的自动增量整数主键。这提供了您所描述的所有好处(以及更多),而几乎在所有方面都优于“顺序指导”。

这很可能会被贬低为遗忘,因为它没有专门回答你的问题(这显然是精心制作的,所以你可以立即自己回答),但我觉得这是一个非常重要的提升点。

答案 2 :(得分:20)

正如massimogentilini已经说过的,使用UuidCreateSequential(在代码中生成guid时)可以提高性能。但似乎缺少一个事实:SQL Server(至少Microsoft SQL 2005/2008)使用相同的功能,但是:Guids的比较/排序在.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 linkthis link

答案 3 :(得分:4)

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

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

来自MSDN

  

重要:
  如果担心隐私,请不要使用此功能。它   有可能猜出的价值   下一次生成GUID,因此,   访问与该GUID相关联的数据。

答案 4 :(得分:4)

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

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

答案 5 :(得分:3)

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

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

答案 6 :(得分:2)

好的,我终于在设计和制作方面达到了这一点。

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

在postgres中插入时,完全随机的UUID和COMB _GUID之间的速度比率对COMB_GUID有利。 对于100万次记录测试,COMB_GUID在我的硬件上通过多次测试的速度 2X 。记录包含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() );
    }

}

答案 7 :(得分:2)

我使用Entity Framework解决了Guid(群集和非群集),Sequential Guid和int(身份/自动增量)之间的区别。与具有同一性的int相比,Sequential Guid的速度惊人地快。 Results and code of the Sequential Guid here

答案 8 :(得分:1)

我不认为是否需要可以猜测唯一键,从Web UI或其他部分传递它们本身似乎是一种不好的做法,如果您有安全问题,我不会看到如何使用guid可以改进一些东西(如果这是使用框架的正确加密函数使用真正的随机数生成器)。
我的方法涵盖了其他项目,可以从代码生成顺序guid而无需DB访问(也适用于Windows),并且它在时间和空间上是独一无二的。
是的,提出问题是为了回答这个问题,给那些为他们的PK选择Guids的人提供一种改善数据库使用的方法(在我的情况下,允许客户在不必更换服务器的情况下维持更高的工作量)。

似乎安全问题很多,在这种情况下不要使用Sequential Guid,或者更好的是,使用标准的Guid for PK,从你的UI传递回来和顺序guid用于其他一切。一如既往没有绝对真理,我也编辑了主要答案来反映这一点。