有人测量过 Sequential Guid 与 Sequential Guid 的性能吗?标准 Guid 用作数据库内的主键时?

有帮助吗?

解决方案

GUID 与顺序 GUID



典型的模式是使用 Guid 作为表的 PK,但是,正如其他讨论中提到的(请参阅 GUID/UUID 数据库密钥的优缺点) 存在一些性能问题。

这是一个典型的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 的表上插入

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

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

DB 文件顺序读取等待时间从 6.4 数以百万计的等待事件 62.415 秒到 1.2 百万个等待事件 11.063 秒。

重要的是要看到所有顺序 guid 都可以被猜测,因此如果考虑到安全性,仍然使用标准 guid,那么使用它们并不是一个好主意。
为了长话短说...如果您使用 Guid 作为 PK,则每次不从 UI 来回传递时都使用顺序 guid,它们将加快操作速度并且不需要任何成本来实现。

其他提示

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

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

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

不幸的是,使用你的建议,你输了 全部 那些事。

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

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

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

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

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

此链接此链接

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

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

来自 MSDN

  

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

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

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

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

是可以使用uniqueidentifier(GUID)作为主键吗?

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

我生成一个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() );
    }

}

我使用Entity Framework解决了Guid(集群和非集群),Sequential Guid和int(身份/自动增量)之间的差异。与具有同一性的int相比,Sequential Guid的速度惊人地快。 顺序指导的结果和代码

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top