随笔 - 80  文章 - 2 评论 - 1197 trackbacks - 37

我的小工具
.NET正则表达式库
中国游戏开发者网络
中国游戏开发者网络 | 论坛

广告


北京狼烟网络科技有限公司
北京市朝阳区曙光西里甲6号 时间国际A座1505&msg=2007101111175778000" frameBorder=0 scrolling=no>

与我联系

搜索

 

常用链接

留言簿(8)

我参与的团队

我的标签

随笔分类

随笔档案

文章档案

相册

友情链接

积分与排名

  • 积分 - 215026
  • 排名 - 156

最新评论

阅读排行榜

评论排行榜

通常,给数据库中的表都添加一个“无意义”的主键,能够大大地简化程序的开发。这个主键用什么类型呢?其实各种类型,只要大小不超过900字节都可以,但我们最常面临的两种选择是——GUID(UniqueIdentifity)和Identity INT。

《ADO.NET 2.0高级编程》一书的“5.2.2 选择主键”一节,对此进行了一些对比,并推荐使用GUID类型作为主键的类型。但本文中老刘将介绍自己在实际开发中的一些感触。

老刘在公司写程序时,面对的数据库绝大多数都是用Identity做主键;而回家自己写程序玩的时候,因为迷信《ADO.NET 2.0高级编程》,所以设计的数据库都采用GUID做主键类型。所以,对这两种主键类型都有些想法。

GUID优于Identify的方面

首先,对于GUID类型的主键,通常在创建新的数据条目时可以采用Guid.NewGuid()方法为其生成主键;而对于Identify类型的主键,只有当数据条目被真正插入数据库后才能得到其主键值。当需要在同一个事务中,向多个相关表中插入数据时,若采用GUID主键,就能使用一次提交动作插入所有数据;而如果使用Identify类型做主键,就必须先插入主键列所在表的数据,再插入外键列所在表的数据。如下面两段代码所示。

// 使用GUID作为主键类型
using(MyDataContext db = new MyDataContext())
{
    Category c 
= new Category
    
{
        Id 
= Guid.NewGuid(),
        Name 
= "分类1"
    }
;
    Article a 
= new Article
    
{
        Id 
= Guid.NewGuid(),
        Title 
= "标题1",
        Body 
= "内容",
        CategoryId 
= c.Id    // Category对象的Id已经确定
    }
;
    db.Categories.InsertOnSubmit(c);
    db.Articles.InsertOnSubmit(a);

    db.SubmitChanges();    
// 一次提交,如果出现错误自动全部回滚
}


// 使用Identity作为主键类型
using(MyDataContext db = new MyDataContext())
{
    Category c 
= new Category    // 由于才用Identity类型作为主键,所以无需也不能明确指定主键值
    {
        Name 
= "分类2"
    }
;
    db.Categories.InsertOnSubmit(c);
    db.SubmitChanges();    
// 提交后才能得到确定的ID值

    Article a 
= new Article
    
{
        Title 
= "标题又来了",
        Body 
= "没有内容",
        CategoryId 
= c.Id
    }
;
    db.Articles.InsertOnSubmit(a);
    db.SubmitChanges(a);    
// 第二个提交,如果出错,分类c也会留在数据库中
}

正如注释中所述,当发生错误时,一次提交可以简单干净地回滚;但多次提交,就得采取一些手段了。

Identify优于GUID的方面

首先,Identify根本上说是一个int,只占4字节;而GUID要占16字节。这当然不算什么,可以忽略。

其次,要知道,主键就是一个簇索引(聚集索引),这意味着数据在磁盘上的物理排列顺序是和主键顺序一样的。这就存在一个问题,每次生成的GUID并不是按照时间顺序从小到大的,换句话说,第二次生成的GUID可能比第一次小,而第三次可能又比第二次小。这就导致每次插入一行数据时,都要对表中整行整行的数据进行排序。而Identify类型的字段,可以明确保障后生成的值比之前生成的值都大,因此新插入的数据会简单地追加在表的尾部。所以,对于频繁插入新数据的表来说,Identify主键的性能要好一些。(注意,本条纯属老刘推测,没有进行过测试。)

最后,Identify主键更便于人类阅读。嗯……给大家各场景吧。在公司,我没有查看生产环境数据库的权限,所以当客户提交来bug之后,我会朝我老板喊:帮我把服务器上ID是1234的用户的数据取出来我看看~~  想想吧,如果采用了GUID主键,我该怎么喊?

-----

注,写到这里,就完了。但我发现好像有些倾向于Identify主键。希望大家不要有这样的偏见,好好看看《ADO.NET 2.0高级编程》,考虑考虑。欢迎大家在这里探讨。

-----

最后还是广告:欢迎访问[.NET正则表达式库] http://regex-lib.net

posted on 2008-07-03 22:38 Anders Liu 阅读(2293) 评论(29)  编辑 收藏

FeedBack:
#1楼  2008-07-03 22:44 Gray Zhang      
在WROX的人头书SQL SERVER入门经典中说到,GUID最大的好处是做数据库复制合并的时候不会冲突~
  回复  引用  查看    
#2楼  2008-07-03 22:52 Ka [未注册用户]
Identify 有爆掉的可能

  回复  引用    
#3楼  2008-07-03 22:54 Kain      
GUID很痛苦的,其实更倾向于GUID加timestamp
  回复  引用  查看    
#4楼  2008-07-03 23:00 A1 [未注册用户]
“ 主键就是一个簇索引(聚集索引)”
你完全可以让主键仅仅作为约束,或者索引,或者两者都是,但非聚集。
对于单个表,聚集索引应该建立那个分割数据最佳选择的字段。这里说的最佳分割是基于IO而言。如果数据表非常巨大,可以考虑根据这个字段分表。
绝大多数的情况下,不管是GUID,还是Identify int都是不应该作为聚集索引的,毫无意义。因为通常的查询都不是需要按插入顺序输出的。
  回复  引用    
#5楼  2008-07-03 23:23 sunnyliang [未注册用户]
这是一个很困扰的人问题,只能按不同的场景和环境来选择了、。

首先,Identify根本上说是一个int,只占4字节;而GUID要占16字节。这当然不算什么,可以忽略。

比如说这个吧,也不是不影响,在一个有数百条选项的下拉框里,一个Guid整整就是8个中文字符,还是蛮客观的。

我没有查看生产环境数据库的权限,所以当客户提交来bug之后,我会朝我老板喊:帮我把服务器上ID是1234的用户的数据取出来我看看~~ 想想吧,如果采用了GUID主键,我该怎么喊?

像业务数据库里的基础表,比如用户啊,客户之类的,我想还是用自定义的编码键来做关键列,比如说一个编码规则之类的,既可以控制长度,而没有Identify的烦恼。

至于业务单证表,一般都用单号就OK。
  回复  引用    
#6楼  2008-07-03 23:40 阿齐      
谁会把Guid放到下拉列表框了让人选呢?

--引用--------------------------------------------------
sunnyliang:
...
比如说这个吧,也不是不影响,在一个有数百条选项的下拉框里,一个Guid整整就是8个中文字符,还是蛮客观的。
...
--------------------------------------------------------

  回复  引用  查看    
有关guid的问题。*
  回复  引用    
#8楼  2008-07-04 00:25 jisen      
我们用GUID,但是吧主键的聚集索引去掉,好像不光有插入性能的损失,还影响查询性能。
  回复  引用  查看    
#9楼  2008-07-04 00:57 Angel Lucifer      
看应用场景。

假如是海量数据库,Identify 最大才多少条数据。很容易就爆掉了。
假如要合并两个数据库数据,Identify 该如何合并?冲突太多了。

不可否认,Identify 有性能上的优势。在中小型数据规模的基础上,又不需要跟其它数据库合并。使用 Identify 是很自然的选择。
  回复  引用  查看    
#10楼  2008-07-04 01:01 Jeffrey Zhao      
guid作主键要用newsequentialid()作为默认值,而不是newid()
  回复  引用  查看    
#11楼  2008-07-04 01:33 深蓝      
我觉得完全没有必要担心使用Identify 会爆掉,如果数据量真的是大的可怕,那可以用bigint的Identify,不用int就是了。
我个人喜欢使用int做主键,使用GUID做主键只有在想防止用户猜测其他主键值的情况下使用。
  回复  引用  查看    
#12楼  2008-07-04 08:36 1-2-3      
还是Oracle里的序列用着爽,为什么SQL Server里没有类似的东东呢?
  回复  引用  查看    
如果数据库是Oracle呢,用啥类型的主键?
Oracle中的guid通过啥来体现呢?
  回复  引用    
#14楼  2008-07-04 09:18 预备役中尉      
各有特色,按具体场景需要考虑使用.
http://www.cnblogs.com/jiangshaofen/archive/2007/04/18/717785.html
  回复  引用  查看    
#15楼  2008-07-04 09:28 pl [未注册用户]
@1-2-3
对啊,序列也是不错的
  回复  引用    
支持楼主
  回复  引用    
#17楼  2008-07-04 10:06 maweishi [未注册用户]
如果有可能可以写个GUID的生成算法,自己生成GUID,我想也不是什么难事,基本可以解决排序问题
  回复  引用    
#18楼  2008-07-04 11:03 xjb      
--引用--------------------------------------------------
Jeffrey Zhao: guid作主键要用newsequentialid()作为默认值,而不是newid()
--------------------------------------------------------

但oracle不支持newsequentialid
  回复  引用  查看    
#19楼  2008-07-04 11:32 luotong      
如果用 DateTime.Now.Ticks 怎么样,不过并发大的话可能会有重复
  回复  引用  查看    
谁会把Guid放到下拉列表框了让人选呢?

--引用--------------------------------------------------
sunnyliang:
...
比如说这个吧,也不是不影响,在一个有数百条选项的下拉框里,一个Guid整整就是8个中文字符,还是蛮客观的。
...
--------------------------------------------------------

有点无语。。。。。。。。
  回复  引用    
#21楼  2008-07-04 13:09 毁于随      
而对于Identify类型的主键,只有当数据条目被真正插入数据库后才能得到其主键值。当需要在同一个事务中,向多个相关表中插入数据时,若采用GUID主键,就能使用一次提交动作插入所有数据;而如果使用Identify类型做主键,就必须先插入主键列所在表的数据,再插入外键列所在表的数据。 ---------------------我不知道楼主的实现是如何的.那个SubmitChanges方法中是将所有的语句组合成一个字符串后提交吗(使用分号)?如果是的话那这个结论没问题.
  回复  引用  查看    
#22楼  2008-07-04 13:10 毁于随      
我曾经看过速达软件对这样主键的处理,他们这两种方案都没有使用,而是自己维护的这个自增ID,不过这东西在复制中确实存在冲突这个问题,而且当了解决.
  回复  引用  查看    
#23楼  2008-07-04 13:58 FF7 [未注册用户]
用GUID做主键是极其愚蠢的做法

小型系统不讨论了,反正怎么折腾都行

大型系统,用GUID做主键简直是恶梦,索引会膨胀得很大,极难维护,一般都会用Sequence做主键,38位长度的整数字是极其难超过的

某国内最著名的B2C网站的商品表就是开始使用GUID做主键,这个设计成了后面所有开发人员和DBA的噩梦,不得不花极大量时间和金钱再改成number

当然,喜欢犯别人已经犯过的错误的人,可以无视
  回复  引用    
同意FF7

=================
首先,Identify根本上说是一个int,只占4字节;而GUID要占16字节。这当然不算什么,可以忽略。
=================

4字节和16字节,分别做聚集索引 所产生的索引分页数量是完全不同的,不管是插入或读取性能都受影响
  回复  引用    
#25楼  2008-07-04 17:22 cw [未注册用户]
这两种都要用到, 关键是分清楚哪里该用.....

>> Identify类型的主键,只有当数据条目被真正插入数据库后才能得到其主键值。

错误观点....主键值不一定是要从表中才能获取....
  回复  引用    
#26楼  2008-07-17 16:38 Artech      
GUID还具有一个明显的优势。当你进行数量的导和导出的时候,ID可以完全保持不变,而Identity就做不到。
或者说我一些数据需要通过Script来插入,而且会涉及到主子表的情况,利用Identity就有麻烦,子表的外键没有办法指定。
至于有人说Identity比GUID具有更好的性能,实际上这种性能的差异是可以接受的。
所以我还是推荐使用GUID。
  回复  引用  查看    
#27楼  2008-08-05 11:40 吴潮槟 [未注册用户]
按楼上发的来看GUID优于Identity只有可以NewGuid()..
这虽然是一个方便,但却要牺牲许多其它的东西,例如主外键关联,虽然也可以关联,但性能差得让你不可能去接受,特别是多表关联的时候。。
Identity的优点就非常明显,
  回复  引用    
#28楼  2008-08-12 10:56 joewang [未注册用户]
数据类型 存储大小(字节) 最小值 最大值
int 4 -2,147,483,648(2G) 2,147,483,647(2G-1)


够大吗?
  回复  引用    
#29楼  2008-08-12 10:57 joewang [未注册用户]
数据类型:int
存储大小(字节): 4
最小值 : -2,147,483,648(2G)
最大值 : 2,147,483,647(2G-1)

这样看的清楚点
  回复  引用    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: