小议数据库主键选取策略
作者:网络转载 发布时间:[ 2015/3/2 16:00:28 ] 推荐标签:数据库 软件开发 字段
二、手动增长型字段
既然自动增长型字段会带来如此的麻烦,我们不妨考虑使用手动增长型的字段,也是说主键的值需要自己维护,通常情况下需要建立一张单独的表存储当前主键键值。还用上面的例子来说,这次我们新建一张表叫IntKey,包含两个字段,KeyName以及KeyValue。像一个HashTable,给一个KeyName,可以知道目前的KeyValue是什么,然后手工实现键值数据递增。在SQLServer中可以编写这样一个存储过程,让取键值的过程自动进行。代码如下:
CREATEPROCEDURE[GetKey]
@KeyNamechar(10),
@KeyValueintOUTPUT
AS
UPDATEIntKeySET@KeyValue=KeyValue=KeyValue+1WHEREKeyName=@KeyName
GO
这样,通过调用存储过程,我们可以获得新键值,确保不会出现重复。若将OrderID字段设置为手动增长型字段,我们的程序可以由以下几步来实现:首先调用存储过程,获得一个OrderID,然后使用这个OrderID填充Order表与OrderDetail表,后在事务保护下对两表进行更新。
使用手动增长型字段作为主键在进行数据库间数据复制时,可以确保数据合并过程中不会出现键值冲突,只要我们为不同的数据库分配不同的主键取值段行了。但是,使用手动增长型字段会增加网络的RoundTrip,我们必须通过增加一次数据库访问来获取当前主键键值,这会增加网络和数据库的负载,当处于一个低速或断开的网络环境中时,这种做法会有很大的弊端。同时,手工维护主键还要考虑并发冲突等种种因素,这更会增加系统的复杂程度。
三、使用UniqueIdentifier
SQLServer为我们提供了UniqueIdentifier数据类型,并提供了一个生成函数NEWID(),使用NEWID()可以生成一个的UniqueIdentifier。UniqueIdentifier在数据库中占用16个字节,出现重复的概率非常小,以至于可以认为是0。我们经常从注册表中看到类似
{45F0EB02-0727-4F2E-AAB5-E8AEDEE0CEC5}
的东西实际上是一个UniqueIdentifier,Windows用它来做COM组件以及接口的标识,防止出现重复。在.NET里管UniqueIdentifier称之为GUID(GlobalUniqueIdentifier)。在C#中可以使用如下命令生成一个GUID:
Guidu=System.Guid.NewGuid();
对于上面提到的Order与OrderDetail的程序,如果选用UniqueIdentifier作为主键的话,我们完全可以避免上面提到的增加网络RoundTrip的问题。通过程序直接生成GUID填充主键,不用考虑是否会出现重复。
UniqueIdentifier字段也存在严重的缺陷:首先,它的长度是16字节,是整数的4倍长,会占用大量存储空间。更为严重的是,UniqueIdentifier的生成毫无规律可言,要想在上面建立索引(绝大多数数据库在主键上都有索引)是一个非常耗时的操作。有人做过实验,插入同样的数据量,使用UniqueIdentifier型数据做主键要比使用Integer型数据慢,所以,出于效率考虑,尽可能避免使用UniqueIdentifier型数据库作为主键键值。
四、使用“COMB(Combine)”类型
既然上面三种主键类型选取策略都存在各自的缺点,那么到底有没有好的办法加以解决呢?答案是肯定的。通过使用COMB类型(数据库中没有COMB类型,它是JimmyNilsson在他的“TheCostofGUIDsasPrimaryKeys”一文中设计出来的),可以在三者之间找到一个很好的平衡点。
COMB数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合的方式,保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与UniqueIdentifier组合起来,在保留UniqueIdentifier的性的同时增加了有序性,以此来提高索引效率。也许有人会担心UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类型数据完全相同的可能性是在这1/300秒内生成的两个GUID前10个字节完全相同,这几乎是不可能的!在SQLServer中用SQL命令将这一思路实现出来便是:
DECLARE@aGuidUNIQUEIDENTIFIER
SET@aGuid=CAST(CAST(NEWID()ASBINARY(10))
+CAST(GETDATE()ASBINARY(6))ASUNIQUEIDENTIFIER)
经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。关于测试数据可以参考我2004年7月21日的随笔。
除了使用存储过程实现COMB数据外,我们也可以使用C#生成COMB数据,这样所有主键生成工作可以在客户端完成。C#代码如下:
///<summary>
///返回GUID用于数据库操作,特定的时间代码可以提高检索效率
///</summary>
///<returns>COMB(GUID与时间混合型)类型GUID数据</returns>
publicstaticGuidNewComb()
{
byte[]guidArray=System.Guid.NewGuid().ToByteArray();
DateTimebaseDate=newDateTime(1900,1,1);
DateTimenow=DateTime.Now;
//Getthedaysandmillisecondswhichwillbeusedtobuildthebytestring
TimeSpandays=newTimeSpan(now.Ticks-baseDate.Ticks);
TimeSpanmsecs=newTimeSpan(now.Ticks-(newDateTime(now.Year,now.Month,now.Day).Ticks));
//Converttoabytearray
//NotethatSQLServerisaccurateto1/300thofamillisecondsowedivideby3.333333
byte[]daysArray=BitConverter.GetBytes(days.Days);
byte[]msecsArray=BitConverter.GetBytes((long)(msecs.TotalMilliseconds/3.333333));
//ReversethebytestomatchSQLServersordering
Array.Reverse(daysArray);
Array.Reverse(msecsArray);
//Copythebytesintotheguid
Array.Copy(daysArray,daysArray.Length-2,guidArray,guidArray.Length-6,2);
Array.Copy(msecsArray,msecsArray.Length-4,guidArray,guidArray.Length-4,4);
returnnewSystem.Guid(guidArray);
}
///<summary>
///从SQLSERVER返回的GUID中生成时间信息
///</summary>
///<paramname="guid">包含时间信息的COMB</param>
///<returns>时间</returns>
publicstaticDateTimeGetDateFromComb(System.Guidguid)
{
DateTimebaseDate=newDateTime(1900,1,1);
byte[]daysArray=newbyte[4];
byte[]msecsArray=newbyte[4];
byte[]guidArray=guid.ToByteArray();
//Copythedatepartsoftheguidtotherespectivebytearrays.
Array.Copy(guidArray,guidArray.Length-6,daysArray,2,2);
Array.Copy(guidArray,guidArray.Length-4,msecsArray,0,4);
//Reversethearraystoputthemintotheappropriateorder
Array.Reverse(daysArray);
Array.Reverse(msecsArray);
//Convertthebytestoints
intdays=BitConverter.ToInt32(daysArray,0);
intmsecs=BitConverter.ToInt32(msecsArray,0);
DateTimedate=baseDate.AddDays(days);
date=date.AddMilliseconds(msecs*3.333333);
returndate;
}
结语
数据库主键在数据库中占有重要地位。主键的选取策略决定了系统是否高效、易用。本文比较了四种主键选取策略的优缺点,并提供了相应的代码解决方案,希望对大家有所帮助。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
在测试数据库性能时,需要注意哪些方面的内容?测试管理工具TC数据库报错的原因有哪些?怎么解决?数据库的三大范式以及五大约束编程常用的几种时间戳转换(java .net 数据库)优化mysql数据库的几个步骤数据库并行读取和写入之Python实现深入理解数据库(DB2)缓冲池(BufferPool)国内三大云数据库测试对比预警即预防:6大常见数据库安全漏洞数据库规划、设计与管理数据库-事务的概念SQL Server修改数据库物理文件存在位置使用PHP与SQL搭建可搜索的加密数据库用Python写一个NoSQL数据库详述 SQL 中的数据库操作详述 SQL 中的数据库操作Java面试准备:数据库MySQL性能优化

sales@spasvo.com