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

对于数据系统表的主键选择不是什么大不了的事 , 可能对于一些朋友来说 , 这非常容易 , 可是却不是如此的简单 , 对于某些应用来说 , 自动加 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 位的上半个字节是固定的 .\

{ 43A 6162C -308A -4112 -86F 8-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 : { 43A 6162C -308A -4112 -86F 8-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 : { 43A 6162C -308A -4112 -86F 8-6E6B6B76FC6E}

<p class="MsoNormal" style="MARGIN: 0c

Published At
Categories with 数据库类
Tagged with
comments powered by Disqus