在数据库中用作主键时,有人曾测量过Sequential Guid与Standard Guid的性能吗?
答案 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
这类数据的问题是:<
-
一种可能的解决方案是使用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;
}
优点
现实生活测量
情形:
实验室测试 - 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而不是自动增量整数的点是:
不幸的是,使用你的建议,你会丢失所有那些东西。
所以,是的。你已经使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);
}
答案 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进行排序,并且在用作主键时,可以在插入新值时减少索引页面拆分。
答案 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用于其他一切。一如既往没有绝对真理,我也编辑了主要答案来反映这一点。