分享
 
 
 

Integer GUID和Comb做主键的效率测试(Delphi+access)(一)

王朝delphi·作者佚名  2006-02-01
窄屏简体版  字體: |||超大  

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

对于不支持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上做了实验,实验证明确实如此,在GUID的16位随机数当中,有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 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 : SConstUnit

//

//Function :

//

//********************************************************************

unit SConstUnit;

interface

uses dateutils;

var

spdelib_base_datetime :TDateTime;

implementation

initialization

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

finalization

end.

当然,你直接给数据值也行,因为在delphi里TDateTime就是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():TGUID;overload;

class Function GetCombGUID(guid:TGUID):TGUID;overload;

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

class Function GetDateTimeFromCombGuid(guid:TGUID):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(10个0),但这样会可能发生溢出

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

//所以这里采用了1000000000(9个0)

//这里的算法是采用的是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)是有区别的,当然这也和操作系统有区别,在windows和unix中日期时间都是整数,这也在用delphi做项目时有了不少的麻烦,因为在delphi将系统时间转化成delphi的格式时进行了除法运算,损失了数据的精度,所以在计算两个日期之间的分钟之差时会有误差,这确实是一个大问题,但是今天终于见到了delphi的这种日期时间格式的一个方便的用处,省得我自已算了.

2. 在delphi中所有的类名前都加”T”,意为Type,呵呵,有点类似于MFC中的”C”,意为”class”,在这里,我的命名方法是在前面加”S”,为了和delphi的本身类库区分,意为:” structure”,当然,如果你认为是”Sinoprise”的首字母,我也不反对.

上面是comb的算法

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有