反驳“小议数据库主键选取策略(转)”

其实你们注意“主键”的同时而忽略了另外一个很重要的东西====〉“索引”
当我们建立一个主键的时候,系统会默认在这个主键上建立一个索引(这里说明一下,我是以MS Sql Server2000为例,其他厂商的数据库我不熟悉,不知道怎么样的),这个索引默认是CLUSTERED索引,也就是聚集索引(聚簇索引)
聚集索引对于数据都会进行排序,然后在索引的页面里面,分别按照数据页里面的索引字建立索引
如下图所示:

但是在数据库中还有另外一种索引:NOCLUSTERED,它是利用一种类似于hashtable的方法,把索引和数据对应起来,非聚簇索引的图示如下:

所以,对于不需要排序的数据,我们使用非聚簇索引相对来说比较好些。
那么对于主键,采用GUID类型的时候,我们应该设置这个主键的索引类型是NOCLUSTERED的,效果就会提高上去
我做了个试验:

CREATE   TABLE   [ tabInt ]  (
    
[ ID ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
    
[ myValue ]   [ int ]   NULL  ,
    
CONSTRAINT   [ PK_tabInt ]   PRIMARY   KEY    CLUSTERED  
    (
        
[ ID ]
    )  
ON   [ PRIMARY ]  
ON   [ PRIMARY ]
GO
CREATE   TABLE   [ tabUnq ]  (
    
[ ID ]   [ uniqueidentifier ]   NOT   NULL   CONSTRAINT   [ DF_tabUnq_ID ]   DEFAULT  ( newid ()),
    
[ myValue ]   [ int ]   NULL  ,
    
CONSTRAINT   [ PK_tabUnq ]   PRIMARY   KEY    CLUSTERED  
    (
        
[ ID ]
    )  
ON   [ PRIMARY ]  
ON   [ PRIMARY ]
GO
CREATE   TABLE   [ tabComb ]  (
    
[ ID ]   [ uniqueidentifier ]   NOT   NULL  ,
    
[ myValue ]   [ int ]   NULL  ,
    
CONSTRAINT   [ PK_tabComb ]   PRIMARY   KEY    CLUSTERED  
    (
        
[ ID ]
    )  
ON   [ PRIMARY ]  
ON   [ PRIMARY ]
GO
CREATE   TABLE   [ tabUnq2 ]  (
    
[ ID ]   [ uniqueidentifier ]   NOT   NULL   CONSTRAINT   [ DF_tabUnq2_ID ]   DEFAULT  ( newid ()),
    
[ myValue ]   [ int ]   NULL  ,
    
CONSTRAINT   [ PK_tabUnq2 ]   PRIMARY   KEY    NONCLUSTERED  
    (
        
[ ID ]
    )  
ON   [ PRIMARY ]  
ON   [ PRIMARY ]
GO



declare  @i bigint
declare  @v  int
declare  @s  datetime
declare  @e  datetime
set  @i = 0
set  @s = getdate ()
print   ' int类型为主键的表插入开始时间: '   +   cast (@s  as   nvarchar )
while  @i < 10000
begin
    
set  @v = rand ()    
    
insert   into  tabInt ( [ myValue ] values  (@v)
    
set  @i = @i + 1
end

set  @e  = getdate ()
print   ' int类型为主键的表插入结束时间: '   +   cast (@e  as   nvarchar )
print   ' int类型为主键的表插入共用时间: '   +   cast  ( DATEDIFF (ms, @s, @e)  as   nvarchar +   ' 毫秒 '

print   '   '
print   '   '


set  @i = 0
set  @s = getdate ()
print   ' GUID(聚簇索引)类型为主键的表插入开始时间: '   +   cast (@s  as   nvarchar )
while  @i < 10000
begin
    
set  @v = rand ()    
    
insert   into  tabUnq ( [ myValue ] values  (@v)
    
set  @i = @i + 1
end

set  @e  = getdate ()
print   ' GUID类型(聚簇索引)为主键的表插入结束时间: '   +   cast (@e  as   nvarchar )
print   ' GUID类型(聚簇索引)为主键的表插入共用时间: ' +   cast  ( DATEDIFF (ms, @s, @e)  as   nvarchar +   ' 毫秒 '

print   '   '
print   '   '


set  @i = 0
set  @s = getdate ()
print   ' GUID(非聚簇索引)类型为主键的表插入开始时间: '   +   cast (@s  as   nvarchar )
while  @i < 10000
begin
    
set  @v = rand ()    
    
insert   into  tabUnq2 ( [ myValue ] values  (@v)
    
set  @i = @i + 1
end

set  @e  = getdate ()
print   ' GUID类型(非聚簇索引)为主键的表插入结束时间: '   +   cast (@e  as   nvarchar )
print   ' GUID类型(非聚簇索引)为主键的表插入共用时间: ' +   cast  ( DATEDIFF (ms, @s, @e)  as   nvarchar +   ' 毫秒 '

print   '   '
print   '   '


DECLARE  @aGuid  UNIQUEIDENTIFIER


set  @i = 0
set  @s = getdate ()
print   ' COMB类型为主键的表插入开始时间: '   +   cast (@s  as   nvarchar )
while  @i < 10000
begin
    
SET  @aGuid  =   CAST ( CAST ( NEWID ()  AS   BINARY ( 10 ))  +   CAST ( GETDATE ()  AS   BINARY ( 6 ))  AS   UNIQUEIDENTIFIER )
    
set  @v = rand ()    
    
insert   into  tabComb ( [ ID ] , [ myValue ] values  (@aGuid,@v)
    
set  @i = @i + 1
end

set  @e  = getdate ()
print   ' COMB类型为主键的表插入结束时间: '   +   cast (@e  as   nvarchar )
print   ' COMB类型为主键的表插入共用时间: '   +   cast  ( DATEDIFF (ms, @s, @e)  as   nvarchar +   ' 毫秒 '

建立了4个表,分别用int(聚簇索引),GUID(聚簇索引),GUID(聚簇索引),以及COMB类型来做为主键
然后分别插入10000条数据
结果如下:
int类型为主键的表插入开始时间:07 19 2004  2:14PM
int类型为主键的表插入结束时间:07 19 2004  2:14PM
int类型为主键的表插入共用时间:7750毫秒
 
 
GUID(聚簇索引)类型为主键的表插入开始时间:07 19 2004  2:14PM
GUID类型(聚簇索引)为主键的表插入结束时间:07 19 2004  2:14PM
GUID类型(聚簇索引)为主键的表插入共用时间:8193毫秒
 
 
GUID(非聚簇索引)类型为主键的表插入开始时间:07 19 2004  2:14PM
GUID类型(非聚簇索引)为主键的表插入结束时间:07 19 2004  2:14PM
GUID类型(非聚簇索引)为主键的表插入共用时间:7540毫秒
 
 
COMB类型为主键的表插入开始时间:07 19 2004  2:14PM
COMB类型为主键的表插入结束时间:07 19 2004  2:14PM
COMB类型为主键的表插入共用时间:7880毫秒


我们会看到,在这里,采用GUID来做主键(非聚簇索引),它的速度是最快的
当然,采用非聚簇索引也有不方便的地方,那就是不能排序了。
MSDN中对于非聚簇索引的应用场景建议如下:
  • 包含大量非重复值的列,如姓氏和名字的组合(如果聚集索引用于其它列)。如果只有很少的非重复值,如只有 1 和 0,则大多数查询将不使用索引,因为此时表扫描通常更有效。
  • 不返回大型结果集的查询。
  • 返回精确匹配的查询的搜索条件(WHERE 子句)中经常使用的列。
  • 经常需要联接和分组的决策支持系统应用程序。应在联接和分组操作中使用的列上创建多个非聚集索引,在任何外键列上创建一个聚集索引。
  • 在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。

而对于聚簇索引,建议的应用场景如下:

  • 包含大量非重复值的列。
  • 使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。
  • 被连续访问的列。
  • 返回大型结果集的查询。
  • 经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。
  • OLTP 类型的应用程序,这些程序要求进行非常快速的单行查找(一般通过主键)。应在主键上创建聚集索引。

所以,在这里,如果只是为了需要一个唯一标示符来做为主键,并且在这个字段上不需要做什么排序呀,范围对比等操作的话,那么我们可以放心大胆的使用GUID来做为主键,但是记得要把它设置成为非聚簇索引。

转载于:https://www.cnblogs.com/zhangpengshou/archive/2007/11/21/966895.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值