| 導購 | 订阅 | 在线投稿
分享
 
 
 

Integer GUID和Comb做主鍵的效率測試(Delphi+access)(一)

來源:互聯網網民  2006-02-01 01:53:39  評論

對于數據系統表的主鍵選擇不是什麽大不了的事,可能對于一些朋友來說,這非常容易,可是卻不是如此的簡單,對于某些應用來說,自動加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的算法

 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
對于數據系統表的主鍵選擇不是什麽大不了的事,可能對于一些朋友來說,這非常容易,可是卻不是如此的簡單,對于某些應用來說,自動加1字段就可以了,但是對于某些系統來說,選擇自動加1 的方式會帶來很大的麻煩,在此種方法的解決上,基本上大多數網友都選擇了GUID做爲主鍵,但是選擇GUID做爲主鍵的方式有一個缺點:大家都知道GUID是一個128位的整數(32*4),他占用的存諸空間是整數的四倍! 在查詢效率方面會不會有很大的影響呢?在這方面,Jimmy Nilsson做了很深的研究(請參見他的著名的文章”The Cost of GUIDS as Primary Keys” [url=http://lists.sqlmagcom/t?ctl=CFBD:83A5C]http://lists.sqlmagcom/t?ctl=CFBD:83A5C[/url]),國內有很多朋友對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的算法
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 
 熱帖排行
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有