SNS论坛技术交流MONO&.NETDatabase Access(Ado.net) Integer GUID和Comb做主键的效率测试

1  /  1  页   1 跳转 查看:1479

Integer GUID和Comb做主键的效率测试

Integer GUID和Comb做主键的效率测试

我早期的测试,仅供参考

对于数据系统表的主键选择不是什么大不了的事,可能对于一些朋友来说,这非常容易,可是却不是如此的简单,对于某些应用来说,自动加1字段就可以了,但是对于某些系统来说,选择自动加1 的方式会带来很大的麻烦,在此种方法的解决上,基本上大多数网友都选择了GUID做为主键,但是选择GUID做为主键的方式有一个缺点:大家都知道GUID是一个128位的整数(32*4),他占用的存诸空间是整数的四倍! 在查询效率方面会不会有很大的影响呢?在这方面,Jimmy Nilsson做了很深的研究(请参见他的著名的文章”The Cost of GUIDS as Primary Keys” [url=http://lists.sqlmagcom/t?ctl=CFBD3A5C]http://lists.sqlmagcom/t?ctl=CFBD3A5C[/url]),国内有很多朋友对GUIDcomb方式进行了比较,但是大多数是在sql平台上做的,对于GUID字段的支持只有Accesssql 支持,其他的数据库可能并不支持这种方式,所我对这种方式进行了如下的测试.


对于不支持GUID字段的数据库,唯一的方式是对GUID值转化成字符串,也就是说最少需要32*8字节来存GUID(他的转化方式是将GUID的十六进制形式转化为了’0..9’’A...F’,Base64方法需要更少的存储空间,但是由于BASE64里面即包含大写字每也包含小写字每,所以不适宜表达GUID).我的测试里面包含了四项 1.自动加1字段,2.GUID字符串,3 comb字符串,4.comb后六个字节放在前面,方便字符串的比较,特别是做主键的时候.


Jimmy Nilsson The Cost of GUIDS as Primary Keys里对GUID的生成做了详细的说明:
“The algorithm for generating GUIDs doesn’t use the MAC address of the network card in recent Windows versions anymore.Instead ,it just creates a random value.In theory,this presents a risk of getting duplicate GUIDs,but,in practice,it shouldn’t be a problem”
“The reason for excluding the use of the MAC address is that it not only couples users to GUIDs,but some network cards don’t use unique MAC address.”


对于这个情况,Jimmy Nilsson还分别在nt 4+sql 7 windows 2000+ sql 2000上做了实验,实验证明确实如此,GUID16位随机数当中,15.5位是随机的,怎么出来个15.5?是这样的,如果你按照半个字节来数的话,13,也就是第7位的上半个字节是固定的.\


{43A6162C-308A-4112-86F8-6E6B6B76FC6E}
也就是这个示例当中的第三组4112中的第一个字符4是固定的, 他代表16进制的4,0100,所以是半个字节.他代表Microsoft.


在我们这个实验中,,GUID的方法肯定会慢,但是慢多少呢?2,3,4理应该是效率一至的,呵呵,你敢确定你的结论吗? Let’s try!


先把算法贴出来吧:
常量定义单元,定义了一个时间的基准值
//********************************************************************
//
// Name
: Sinoprise Function Library ForDelphi
//
//Author
:
Shuguang Yin

//Create
:
2005-11-15

//
// Copyright (c) 2005 : Sinoprise Technology Lab
// Official WebSite
: http://www.sinoprise.com

// Sinoprise Technology Community : http://www.winux.cn
//
//********************************************************************
//
//Unit Name : SConstUnit
//
//Function :
//
//********************************************************************
unit SConstUnit;


interface
uses dateutils;
var

spdelib_base_datetime DateTime;

implementation
initialization

spdelib_base_datetime := EncodeDateTime(2000,1,1,0,0,0,0);

finalization


end.


当然,你直接给数据值也行,因为在delphiTDateTime就是double类型.这只是个人习贯而以.


定义系统服务单元:提供了系统的comb算法:


//********************************************************************
//
// Name
: Sinoprise Function Library For Delphi

//
//Author
:
Shuguang Yin

//Create
:
2005-11-15

//
// Copyright (c) 2005 : Sinoprise Technology Lab
// Official WebSite
: http://www.sinoprise.com

// Sinoprise Technology Community : http://www.winux.cn
//
//********************************************************************
//
//Unit Name : SSystemUnit
//
//Function : The System Functions
//
//********************************************************************
unit SSystemUnit;


interface


uses Windows,ActiveX,SysUtils,Dateutils;


const Cardinal_Length = sizeof(Cardinal);


type



SSystem = class




//
取得一个经过转化的GUID,详见:Jimmy Nilsson'The Const of GUIDs as Primary Keys'


class Function GetCombGUID()GUID;overload;


class Function GetCombGUID(guidGUID)GUID;overload;


//
comb guid中得到日期和时间


class Function GetDateTimeFromCombGuid(guidGUID):TDateTime;




//
根据给定的字符串分割符 ,格式化GUID的字符串


class Function GetGuidString(guid:TGUID;beginSymbol:string='{';


endSymbol:string='}';partitionSymbol:string='-'):string;


class Function GetGuidString2(guid:TGUID;beginSymbol:string='{';


endSymbol:string='}';partitionSymbol:string='-'):string;


end;



implementation
uses SConvertUnit,SConstUnit;


{ SSystem }


class function SSystem.GetCombGUID: TGUID;
begin

if CoCreateGUID(result) = S_OK


then begin


result := GetCombGUID(result);


end


else begin


raise Exception.Create('Create GUID Err!');


end;

end;


class function SSystem.GetCombGUID(guid: TGUID): TGUID;
var

dtm : TDateTime;


days,seconds,tmp : longint;//32 bit

begin

dtm := Now();


dtm := dtm - spdelib_base_datetime;


days := trunc(dtm);


//
应该*1000000000(100),但这样会可能发生溢出


//
如果采用int64,则会给下面的移位转换带来麻烦


//
所以这里采用了1000000000(90)


//
这里的算法是采用的是Jimmy Nilsson'The Const of GUIDs as Primary Keys'


//
算法,对于实时性要求不太高的系统来说,可以采用3/3,


//3
个字节存放日期,3个字节存放时间,这里是2/4


seconds := trunc(( dtm - days ) * 1000000000);




//
高位在前


//
整数部分(日期部分)


tmp := days and $FF;


guid.D4[3] := byte(tmp);


tmp := (days shr 8);


guid.D4[2] := byte(tmp);




//
小数部分(时间部分)


tmp := seconds and $FF000000;


tmp := tmp shr 24;


guid.D4[4] := byte(tmp);




tmp := seconds and $00FF0000;


tmp := tmp shr 16;


guid.D4[5] := byte(tmp);




tmp := seconds and $0000FF00;


tmp := tmp shr 8;


guid.D4[6] := byte(tmp);




tmp := seconds and $000000FF;


guid.D4[7] := byte(tmp);




Result := guid;

end;


class function SSystem.GetDateTimeFromCombGuid(guid: TGUID): TDateTime;
var

days,seconds : longint;//32 bit

begin

days := guid.D4[2];


days := days shl 8;


days := days + guid.D4[3];


days := days + trunc(spdelib_base_datetime);




seconds := guid.D4[4];


seconds := seconds shl 8;


seconds := seconds + guid.D4[5];


seconds := (seconds shl 8) + guid.D4[6];


seconds := (seconds shl 8) + guid.D4[7];


result := seconds / 1000000000;




result :=days + result;

end;


class function SSystem.GetGuidString(guid: TGUID; beginSymbol, endSymbol,

partitionSymbol: string): string;

begin



//Get a guid string like this : {43A6162C-308A-4112-86F8-6E6B6B76FC6E}


Result := GUIDToString(guid);




Result := beginSymbol + Copy(Result,2,8)


+ partitionSymbol + Copy(Result,11,4)


+ partitionSymbol + Copy(Result,16,4)


+ partitionSymbol + Copy(Result,21,4)


+ partitionSymbol + Copy(Result,26,12) + endSymbol;



end;


class function SSystem.GetGuidString2(guid: TGUID; beginSymbol, endSymbol,

partitionSymbol: string): string;

begin

//Get a guid string like this : {43A6162C-308A-4112-86F8-6E6B6B76FC6E}


Result := GUIDToString(guid);




Result := beginSymbol + Copy(Result,26,8)


+ partitionSymbol + Copy(Result,34,4)


+ partitionSymbol + Copy(Result,16,4)


+ partitionSymbol + Copy(Result,2,4)


+ partitionSymbol + Copy(Result,6,4)



+ Copy(Result,11,4)+ Copy(Result,21,4)+ endSymbol;

end;


end.


这里有两点要说明
1.
delphi,日期时间有表示不是整数,这和c系列(c,c++,c#,java)是有区别的,当然这也和操作系统有区别,windowsunix中日期时间都是整数,这也在用delphi做项目时有了不少的麻烦,因为在delphi将系统时间转化成delphi的格式时进行了除法运算,损失了数据的精度,所以在计算两个日期之间的分钟之差时会有误差,这确实是一个大问题,但是今天终于见到了delphi的这种日期时间格式的一个方便的用处,省得我自已算了.
2.
delphi中所有的类名前都加”T”,意为Type,呵呵,有点类似于MFC中的”C”,意为”class”,在这里,我的命名方法是在前面加”S”,为了和delphi的本身类库区分,意为:” structure”,当然,如果你认为是”Sinoprise”的首字母,我也不反对.


上面是comb的算法

Sinoprise Network Studio
 

回复: Integer GUID和Comb做主键的效率测试

看一下我们的表格构:









TInteger表是用自动加1字段做为主键(PInt)
TGuid表是用GUID做为主键(PGUID)
PComb表是用Comb做为主键(PComb)
PComb2表是用改过后的Comb做为主键(PComb,第四种情况)
在这四表中还有一列,NInt,用于插入一个数值,随机的,没有什么实际的意义







下面先看一下我们的测试界面:












插入数据是指在数据库的四个表中插入指定条目的数据.
Count(*)是在四个表中用select cocunt(*) as rc from table做统计查询
Count是在四个表中用select cocunt(主键) as rc from table做统计查询







Label3,Label5,Label7,Label9分别是四个表所运行的时间.







下面是测试程序:







unit Unit1;







interface







uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,


Dialogs, StdCtrls,Activex,SSystemUnit, ADODB, DB;








type

TForm1 = class(TForm)


ADOConnection1: TADOConnection;


ADOCommand1: TADOCommand;


Edit1: TEdit;



Button1: TButton;


Label1: TLabel;


Label2: TLabel;


Label3: TLabel;


Label4: TLabel;


Label5: TLabel;


Label6: TLabel;


Label7: TLabel;


Button2: TButton;


Button3: TButton;


Label8: TLabel;


Label9: TLabel;


procedure Button1Click(Sender: TObject);


procedure Button2Click(Sender: TObject);


procedure Button3Click(Sender: TObject);


private


{ Private declarations }


procedure InsertComb(cc : integer);


procedure InsertComb2(cc : integer);


procedure InsertGuid(cc : integer);


procedure insertInt(cc : integer);


public


{ Public declarations }


end;








var

Form1: TForm1;








implementation







{$R *.dfm}







procedure TForm1.Button1Click(Sender: TObject);
var

TotalTime : Cardinal;


cc : integer;

begin
cc:= strtoint(Edit1.Text);
TotalTime := GetTickCount;
insertInt(cc);
TotalTime := GetTickCount - TotalTime;
Label3.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
InsertGuid(cc);
TotalTime := GetTickCount - TotalTime;
Label5.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
InsertComb(cc);
TotalTime := GetTickCount - TotalTime;
Label7.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
InsertComb2(cc);
TotalTime := GetTickCount - TotalTime;
Label9.Caption := inttostr(TotalTime);
end;







procedure TForm1.InsertComb(cc : integer);
var

i : integer;

begin

ADOCommand1.CommandText :='Insert into TComb(PComb,NInt) values(:PComb,:NInt)';


ADOCommand1.Prepared := true;


For i:= 0 to cc-1 do


begin


ADOCommand1.Parameters[0].Value := SSystem.GetGuidString(SSystem.GetCombGUID(),'','','');


ADOCommand1.Parameters[1].Value := i/7;


ADOCommand1.Execute;


end;








end;







procedure TForm1.InsertComb2(cc: integer);
var

i : integer;


//combStr : string;

begin

ADOCommand1.CommandText :='Insert into TComb2(PComb,NInt) values(:PComb,:NInt)';


ADOCommand1.Prepared := true;


For i:= 0 to cc-1 do


begin


//combStr :=


//combstr := copy(combstr,21,32)+copy(combstr,1,20);


ADOCommand1.Parameters[0].Value := SSystem.GetGuidString2(SSystem.GetCombGUID(),'','','');;


ADOCommand1.Parameters[1].Value := i/7;


ADOCommand1.Execute;


end;








end;







procedure TForm1.InsertGuid(cc : integer);
var

i : integer;


guid : TGUID;

begin

ADOCommand1.CommandText :='Insert into TGuid(PGuid,NInt) values(:PGuid,:NInt)';


ADOCommand1.Prepared := true;


For i:= 0 to cc-1 do


begin


CoCreateGUID(guid);


ADOCommand1.Parameters[0].Value := SSystem.GetGuidString(guid,'','','');


ADOCommand1.Parameters[1].Value := i/7;


ADOCommand1.Execute;


end;








end;







procedure TForm1.insertInt(cc : integer);
var

i : integer;

begin

ADOCommand1.CommandText :='Insert into TInteger(NInt) values(:NInt)';


ADOCommand1.Prepared := true;


For i:= 0 to cc-1 do


begin


ADOCommand1.Parameters[0].Value := i/7;


ADOCommand1.Execute;


end;








end;







procedure TForm1.Button2Click(Sender: TObject);
var

TotalTime : Cardinal;

begin
TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(*) as ttc from TInteger';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label3.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(*) as ttc from TGuid';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label5.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(*) as ttc from Tcomb';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label7.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(*) as ttc from Tcomb2';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label9.Caption := inttostr(TotalTime);
end;







procedure TForm1.Button3Click(Sender: TObject);
var

TotalTime : Cardinal;

begin
TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(PInt) as ttc from TInteger';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label3.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(PGuid) as ttc from TGuid';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label5.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(PComb) as ttc from Tcomb';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label7.Caption := inttostr(TotalTime);







TotalTime := GetTickCount;
ADOCommand1.CommandText :='select count(PComb) as ttc from Tcomb2';
ADOCommand1.Execute;
TotalTime := GetTickCount - TotalTime;
Label9.Caption := inttostr(TotalTime);
end;







end.







很简单的代码,无需加说明了.



































































Sinoprise Network Studio
 

回复: Integer GUID和Comb做主键的效率测试

下面是测试数据
插入数据测试
1.
一次插入100条记录(单位:毫秒):
Integer
GUID
Comb
Comb2

60
90
30
40

70
80
81
50

60
90
50
50

60
80
80
70

70
81
80
80

61
80
80
60

30
40
40
30



在较多次情况下,Comb2<Integer<Comb<Guid


2.
一次插入1000条记录(单位:毫秒):
Integer
GUID
Comb
Comb2


340
361
390
351


451
340
341
320


451
340
331
330

391
341
330
320

421
350
331
330

440

351
420
331

471
350
331
330

471
4086
571
420



在较多的情况下 Comb2<Comb<GUID<Integer
但是GUID的那个4086,不好解释,唯一的可能就是当时的我系统忙,我也没有做别的呀,仅运行这个测试用例而以.


3.
一次插入10000条记录(单位:毫秒):
Integer
GUID
Comb
Comb2

2954
3545
3405
3325

2984
3665
3505
3415

3035
3845
3676
3445

3114
3956
3575
3525

3505
8693
4316
3936

3305
4456
4216
3535



在较多的情况下 Integer<Comb2<Comb<Guid


4,补测1, 一次插入20000(单位:毫秒):
Integer
GUID
Comb
Comb2

16634
42051
45504
16444

17345
21541
19318
17555

15292
20349
34399
17716



已经说明情况了,GUIDComb(都是字符串)因为前面的字符没有序,且又是主键,才会出现这种情况,他们两个应该是第三和第四了

Integer<Comb2



5补测1, 一次插入20记录(单位:毫秒):
Integer
GUID
Comb
Comb2

561
80
20
30

20
20

40
30

10
40
40
30

10
90
30
20

20
21
20
20

20
80
40
20



好了,比较稳定的还是integercomb2,在第一次启动的时候Integer耗时大了一些,基本上还是这个次序 Integer<Comb2<comb<guid


查询操作
1.
Count(*)(
单位:毫秒):

Integer
GUID
Comb
Comb2


1703
80
40
1202


10
20
20
10


40
10
20
20


10
20
10
10

10
10
20
20

10
20
10
20

以下数据,每行都代表程序程序重新启动
100
40
30
40

30
30
40
40

250
60
10
10

10
20
0
0
况然出现两个0!

20
20
30
40

以下数据是在程序进行补测之后的数据(多出了6W多条数据)


1361
832
140
12858

30
70
121
70

140
60
70
70

20
80
70
60

20
111
70
70

120
120
60
70

20
70
70
60

320
61
70
70

20
100
60
70



也显示出效果来了,虽然integer的表现有大起大落,不稳定,可是也拿第一了,对于GUIDcomb,comb2表现倒相差不多,特别是comb2,comb数据十分稳定,至于第一次耗时较多是因为更新数据之后在更新索引,在这方面integer也没有什么优势,虽然补测之comb2integer差了很大一块,可是guidcomb的数据确比integer好多了.


总体优势:Integer<Comb2<comb<GuID


2.
Count(
单位:毫秒):
Integer
GUID
Comb
Comb2


30
30
31
40


10
20
10
20

10
20

20
10

40
20
10
20


10
10
20
10

10
20
20
10

10
10
20
10

以下数据是在程序进行补测之后的数据(多出了6W多条数据)
30
60
80
70

290
71
60
70

20
70
60
70

20
111
60
70

30
100
71
70

20
110
70
71

311
60
70
80

211
170
60
70

20
120
60
70

总体来说还是integer比较有优势 comb,comb2,guid也相差不多,只不过不知道何故IntegerGUID在查询效率上不稳定.Comb,comb2确一直很稳定.


结论,Integer最好的情况下,comb2,comb,GuidInteger耗时的1~3,comb2,comb,guid三种情况里,也属comb2最为稳定,也最有效率.

Sinoprise Network Studio
 

回复: Integer GUID和Comb做主键的效率测试

在做完测试后,access的数据库大小:






测试完成后,数据库的记录总量






数据库中四个表共有记录:134,644 * 4

我的系统配置:







Sinoprise Network Studio
 
1  /  1  页   1 跳转

版权所有 Sinoprise Network Studio   Sitemap

Powered by Discuz!NT 2.0.1214    Copyright © 2001-2008 Comsenz Inc.
Processed in 0.484375 second(s) , 3 queries.
返顶部