软件真谛

用水云般自在的禅心,书写诗情画意的程序人生...
posts - 25, comments - 984, trackbacks - 20, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 ::  :: 管理

主键的故事

Posted on 2008-05-12 08:19 李战 阅读(8235) 评论(86)  编辑 收藏 所属分类: 数据库
    1969年8月8日,在北京协和医院降生了一个漂亮的小女孩。接生的阿姨说,她的声音这么大,好象想要全世界的人都听到。

    后来,她的父亲为她取了一个很好听的名字,叫“王菲”。于是,所有的小朋友就叫她“王菲”,“王菲”就是她童年的主键。

    在她上初二的时候,认识了二班另一个叫“王菲”的同学,而且和她同一天生日。不过,同学们常常将她俩弄错,后来,就分别叫她们“大王菲”和“小王菲”。“大王菲”就是她在同学们心目中的主键。

    在大王菲18岁的那一年,她领到了她的身份证。从此,她有了新的身份标识“100321690808022”,这一标识可以唯一区别中国大陆的每一个人。同时,原来二班的“小王菲”也领到了她自己的身份证,“100321690808006”。于是,人们就可以用身份证号,唯一标识两个“王菲”了。身份证号就是她成年后的主键。

    由于她的歌唱的非常好,没多久就成了歌星。后来她去了香港发展,娱乐公司非要将她的名字更改为“王靖雯”,虽然她并不喜欢这个名字,但为了事业的发展还是同意了。“王靖雯”就成了她在娱乐圈里的主键。

    “王靖雯”的歌打动了许许多多的歌迷,很快就成了歌坛天后。歌迷们将“王靖雯”这一主键与无数条动听的歌曲记录关联在了一起,并形成了一个庞大的歌迷数据库。

    没多久,王靖雯和一个弹电吉他的小子相爱了。那小子说,还是“王菲”这个名字好听,于是,“王靖雯”又变回“王菲”了。主键被那小子修改了,这下麻烦大了。歌迷们都糊涂了,是将她的歌关联到“王菲”还是“王靖雯”呢?这一主键的修改,导致歌迷数据库的一次大混乱。

    再后来呢,她和那个弹电吉他的小子结婚了,香港政府将他们的身份证号码,用结婚证书关联起来。这本也算一桩好事,可是,月老在酒醒之后发现了这一错误,就将关联的记录删除了。阴差阳错,命运使然,她和那个弹电吉他的小子只好分手了。

    但是,正如接生的阿姨说的那样,她的声音的确让全世界的人都听到了!

    讲完这个故事之后,我们是否能领悟到数据库设计的一些哲理呢?

    什么是主键?

    关系数据库说,为了唯一区分表的每一行记录,必须为表确定一个主键。主键可以是一个或多个列组成,这些主键列的值不能重复。主键是两个表进行关联的基础,所谓“关系”体现的是一个表的字段与另一个表的主键的关联。

    在日常的应用项目开发中,我们常常都会面临“该拿什么字段来做主键”的难题。

    用名字做主键?一班的“王菲”和二班的“王菲”可能重复。

    用身份证号做主键?在大陆也许不会重复,去香港后,人家却不认。本不相信第二代身份证号会重复,但俺的一位同事正好撞上!

    同样,主键值的更新也是麻烦事儿。“王靖雯”变成“王菲”那段日子,货架上一张唱片是王靖雯的,另一张又是王菲的,许多歌迷还以为又有新人出现了。

    请不要说可以级联更新,将你那个做主键的字段加长两位试试?想当年,当身份证号从15位增加到18位时,给银行、证券、政府、企业...等多条战线上的兄弟们制造了怎样的痛苦啊!

    为什么会出现这些问题呢?

    原因就是:错误地把对象属性当作对象标识!

    对象属性和对象标识都是对象数据库中的概念。

    什么是对象属性呢?

    就是业务逻辑上涉及的任何可变信息,什么“姓名”、“性别”、“身份证号”、“订单号”...通通都是对象属性。对象属性总会变化的,只是有些变得快,有些变得慢而已。

    对象标识是啥?就是唯一区分数据对象的鉴别符,对象标识存在的唯一目的就是区分对象,除此之外没有任何业务逻辑上的意义。

    不管王菲的属性值怎样变化,但王菲还是王菲,不是二班的那个“王菲”。也就是说,王菲的灵魂未变,她是不会改变的,就象哲学上所说的“不以人的意志为转移”。这种唯一表示对象本身的东西,就是对象标识!

    对象标识是唯一的。也就是说,即使两个对象,他们的属性完全一样,但它们的对象标识是不同的。毕竟,同名同姓甚至同一天出生的大王菲和小王菲是两个不同的人。

    对象的标识是永恒不变的。一旦对象产生,它的标识就自然地、唯一地产生了。尽管王菲换了名,身份证号也变过,但王菲的对象标识未变。即使到了下个世纪,她的对象标识也将依然存在于歌迷们的们的心中。

    对象的标识是描述关系的基础。王菲唱的歌是王菲唱的,不是初二二班的那个“王菲”唱的。王靖雯唱的歌就是王菲唱的歌,有的歌迷只将歌曲和歌手的人名关联起来,难怪会出混乱。香港政府也犯相同的错误,将王菲的身份证号码这一内部属性,跟那个弹电吉他的小子关联起来,也许就是命运的错误。

    那么,我们在设计数据库结构时,到底该用什么来做主键呢?

    对象数据库说,主键只能是对象标识!

    至于对象属性是否唯一,那是由业务逻辑所决定的。如果业务逻辑规定订单号不能重复,就为订单号建一个唯一索引好了,但它不是主键。

    所以,当我们看到有些数据库设计采用了额外的一个字段来专门充当主键,并用这个主键与其他表关联的话,那就是已经走到对象数据库的门口了。什么“内部码”,“流水号”,“序列号”,“自增数”...通通都可以算是对象标识。

    为什么SQL Server要提供自增量字段以及GUID的标识字段呢?都是为了这专门的主键字段服务的。

    如果我们打算向对象数据库路上走得话,就请使用对象标识来做专门的主键字段吧。

    当然,怎样产生唯一的对象标识来做主键,这也是有说道的。

    1.用自增量字段

    自增量字段每次都会按顺序递增,可以保证在一个表里的主键不重复。除非超出了自增字段类型的最大值并从头递增,但这几乎不可能。使用自增量字段来做主键是非常简单的,一般只需在建表时声明自增属性即可。

    自增量字段的长度可以很短,比如使用一个int类型就基本上够用了。简短的主键可以在大量数据和复杂的关系查询中表现出更好的性能。

    自增量的值都是需要在系统中维护一个全局的数据值,每次插入数据时即对此次值进行增量取值。当在当量产生唯一标识的并发环境中,每次的增量取值都必须最此全局值加锁解锁以保证增量的唯一性。这可能是一个并发的瓶颈,会牵扯一些性能问题。

    还有,如果要搞分布式数据库的话,这自增量字段就有问题了。因为,在分布式数据库中,不同数据库的同名的表可能需要进行同步复制。一个数据库表的自增量值,就很可能与另一数据库相同表的自增量值重复了。当然,这可以通过指定不同的递增起始值来错开,但总觉得不爽啊。

    SQL Server中还可以使用timestamp类型的数据来做对象标识符,这可以使对象标识对整个数据库都是唯一的,而不仅仅是对表唯一。但其优缺点与表的自增字段一样。

    2.随机数字段

    随机生成对象标识的方法实际就是碰运气。按照某种复杂的随机算法迅速产生对象标识,碰一碰对象标识不重复的运气。只要这种算法产生的对象标识一万年才可能重复一次,那你就可以在实际开发中应用这种算法。比如,SQL Server中提供了uniqueidentifier类型,配合NEWID()函数来产生GUID,基本上可以应用于所有主键需求了。

    随机生成标识不需要在系统中维护全局量,不存在自增字段那种加锁解锁的性能开销,对于大量的并发处理来说是个福音。

    同时,即使在分布式数据库应用中,不同数据库产生的随机值也是不同的。因此,在数据库的同步复制中,标识相同的就一定是同一条记录,就不会存在产生主键冲突的问题了。

    不过,为了保证随机的唯一性,需要大范围的数值空间。这也使得主键字段比较大,在关联查询的时候有一定的性能损失,判断GUID值是否相同总比判断int值是否相同要多费些功夫。

    在实际应用中到底该用什么方案来产生对象标识需要根具体情况决定,以上方案仅仅是理论探讨。

    接下来要注意的就是主键的索引结构的优化了。

    主键都要整成聚集索引。聚集索引在SQL Server内部就是聚集表,聚集表是B树结构,索引值存在B树的中间节点中,而数据行就存放在B树的页节点上。也就是说,聚集索引和数据表其实是一个统一的整体结构。因此,聚集索引查找和定位数据的效率要比一般索引高出很多。

    此外,专门主键字段值是永远都不变。聚集索引建值不变,聚集表的节点也不会调整,硬盘上的数据记录块基本不动窝的。这可以极大减少数据库碎片,除非删除数据行。数据库的碎片少了,查询数据自然要快些。

    一般来说,我们存入数据库中的数据总是有时间顺序的,我们日常查询和使用的数据也总是近期的数据。有的常用查询甚至总是按时间顺序倒排,比如,邮件列表,论坛帖子。如果主键采用的是自增字段,我们不妨将增量值设为-1或干脆搞成倒序的主键。这样,查询的排序与扫描索引的顺序相同,毕竟硬盘的磁头总喜欢从前往后读,这会稍微提高一点读取聚集索引的速度。

    同样,如果使用随机主键值的方案,我们也建议采用与时间相关的随机数值,而不是GUID。与时间相关的随机数就是,虽然主键是随机产生的,但后产生的随机数应该大于先产生的数。

    为什么不用GUID呢?因为它生成的值就像顽皮的猴子,到处乱跳。于是,在每次插入记录时,聚集索引节点中相邻的值并不具有时间上的顺序。而当我们习惯性地查询近期数据时,硬盘的磁头也需要像猴子般乱跳一通之后才能读到按时间顺序的所有数据。

    如果采用有时间顺序的随机值,聚集索引插入数据时总往一个方向增加数据行的页节点,与同一时期的数据行几乎总相邻。查询同期数据时,磁头就很少乱跳了。磁头一次可以读取一批数据,效率有将有所提升。如果您用电子显微镜去观察硬盘的表面,聚集索引的那颗B树将生长得很正很齐。

    对磁头读写的优化,是完美主义的程序员所追求的,是否采用完全看自己的心态。总之,高兴就好。当然,等将来的大容量存储设备都用上固态盘了,没磁头了,全电信号了,这些优化就都没有什么意义了。

    所以呢,追求完美也是很痛苦的。吃了程序员这碗饭,就只能被IT的洪流卷着往前走。我们不过是这洪流中的一粒沙子,不知道又会被带到何方?

    王菲在一首歌中这样唱到:一路上有人太早看透生命的线条命运的玄妙,有人太晚觉悟冥冥中该来则来无处可逃...

原创作者:李战(leadzen).深圳 2008-5-10
原文地址:http://www.cnblogs.com/leadzen/archive/2008/05/10/1191010.html
【转载请注明作者及出处】

Feedback

#1楼    回复  引用  查看    

2008-05-10 11:16 by 冬冬      
呵呵,写的好!

#2楼    回复  引用  查看    

2008-05-10 11:18 by Indigo Dai      
你写文章的风格蛮独特啊……

#3楼    回复  引用    

2008-05-10 11:40 by 王菲中文论坛 [未注册用户]
楼主也喜欢王菲吗

#4楼    回复  引用  查看    

2008-05-10 11:45 by 开心每一天ㄨ      
程序员..

眼中只有主键
字段.


.好累...

#5楼    回复  引用  查看    

2008-05-10 11:56 by 侯垒      
学习了。

#6楼    回复  引用  查看    

2008-05-10 12:05 by 金色海洋(jyk)      
写得挺生动的,文才很好,学习学习。

对于主键,我很同意你的意思,但是关于磁头的,就有点疑问了。

“我们不妨将增量值设为-1或干脆搞成倒序的主键。这样,查询的排序与扫描索引的顺序相同,毕竟硬盘的磁头总喜欢从前往后读,这会稍微提高一点读取聚集索引的速度。”

我觉得完全不用担心这一点,差也差不了多少。

#7楼    回复  引用  查看    

2008-05-10 12:06 by 金色海洋(jyk)      
程序员..

眼中只有主键
字段.


如果真的是这样的话,我觉得很轻松。

如果眼里都是对象的话,那么我觉得也会很轻松。

问题是,当对象和数据库发生关系的时候,痛苦就产生了。

#8楼    回复  引用  查看    

2008-05-10 12:19 by Justin      

#9楼    回复  引用  查看    

2008-05-10 12:32 by WGforward      
写的很好。学习了,自己以前只用过“自增量字段”来做主键。

#10楼    回复  引用  查看    

2008-05-10 12:51 by Zhuang miao      
楼主干嘛拿王菲说事?

#11楼 [楼主]   回复  引用  查看    

2008-05-10 13:03 by 李战      
@金色海洋(jyk)

您批评得是。

磁头的问题俺也不太懂,“按磁头读取顺序存放数据可以提高硬盘读取效率”俺也是听他人说的。俺也没法证实这种说法,就写了“稍微提高”四个字。您说的有道理,现在也说不定有瞬间刹车并高速倒转的硬盘了,只是俺孤陋寡闻而已。您是专家,俺听您的。

俺民科,电子显微镜俺更是见都没见过,惭愧啊。关于B树的长相,完全胡乱猜想,未经严谨的科学求证。那也是听人说“按顺序插入的索引树节点更相邻”,谁知道对不对呢?

俺都有十几年没碰过硬件了,那个年代,做块汉卡吧,跟砖头似的;搞个RAM盘吧,才512K大。最多也就焊了几块CPU,烧坏两块286。估计现在的空中加油机早就不用原来那块老电路板了吧。最近一次碰硬件,也就是从几角旮旯里找了块120M硬盘,和儿子一起砸核桃吃来着。现在要说起“硬件”两个字,脸都发烫啊。

俺忽悠这些,不过是想骗点点击。说不定将来挂个google广告,赚点小钱啥的。

多谢指点


@Zhuang miao
不瞒您说,俺以前暗恋她啊。

#12楼    回复  引用  查看    

2008-05-10 13:18 by Jeffrey Zhao      
聚合索引主键还是使用NEWSEQUENCEID()比较好

#13楼    回复  引用  查看    

2008-05-10 13:40 by Anders Cui      
写得好!

#14楼 [楼主]   回复  引用  查看    

2008-05-10 13:48 by 李战      
@Jeffrey Zhao
赵老师说得对。

虽然NEWSQUENCEID()不能直接调用,只能用在创建表时给字段指定默认值里,但它能保证主键有顺序性。

不过,俺用的主键是自己写的一个SQL函数,返回有时间顺序的bigint值。在您面前,俺就不好意思拿出来秀了。

#15楼    回复  引用  查看    

2008-05-10 13:50 by charry      
实在是太有才了

#16楼    回复  引用    

2008-05-10 13:58 by abcde [未注册用户]
博主的老文章了
若干年前,满世界的找博主的文章看,
现在老窝在这了,有福了。。。。。。

#17楼    回复  引用    

2008-05-10 14:01 by 俺是农民 [未注册用户]
因为它生成的值就像顽皮的猴子,到处乱跳。这不是楼主的一贯作风么?哈哈,开个玩笑了!莫生气哦!

#18楼 [楼主]   回复  引用  查看    

2008-05-10 14:03 by 李战      
@abcde
确实,这篇本来是为十年前的《悟透DELPHI》写的,丢了可惜。改编了一下,加点郫县豆瓣,炒了盘回锅肉。

#19楼    回复  引用  查看    

2008-05-10 14:11 by 狼Robot      
遗憾,都到18楼了我才回来看到。沙发早没了。

每次看李老师的文章都学得受益匪浅。

#20楼    回复  引用  查看    

2008-05-10 14:26 by 求知无傲      
李老师文采飞扬啊。呵呵。

#21楼    回复  引用    

2008-05-10 15:13 by jingiqin [未注册用户]
@Jeffrey Zhao
是NEWSEQUENTIALID(),拼写错误 :)

#22楼    回复  引用    

2008-05-10 15:52 by Duron800 [未注册用户]
对,老赵拼错了。

#23楼    回复  引用  查看    

2008-05-10 17:13 by 怪怪      
@李战
主键生成的算法, 记得似乎以前博客园有讨论, 不妨把大家的东西都翻出来看看 :)

对啦, 老哥多大岁数了? 我一直以为你那啥, 那啥啥呢...

#24楼    回复  引用  查看    

2008-05-10 17:14 by 生鱼片      
学习了

#25楼    回复  引用  查看    

2008-05-10 18:25 by 大门      
有软件真谛的味道,另外,主键究竟用啥样的好。
我现在就用自增INT,你们都用啥。

#26楼    回复  引用  查看    

2008-05-10 18:50 by 冬虫草      
才华

#27楼    回复  引用  查看    

2008-05-10 19:45 by 随风逝去(叶进)      
博主确实有点意思!
主键尽量跟时间搭界,插入的时候最好是时间越近的在越前面! ——受用了!

#28楼 [楼主]   回复  引用  查看    

2008-05-10 20:07 by 李战      
@怪怪
多谢怪怪老师指点。

俺这也是十年前的老方法,现在也许都过时了。相信园子里的老师们都已经有更好的解决方案了,俺初来园子混,不知深浅。俺一定要把老师们的帖子翻出来好好学习,刷新脑袋里的BIOS。

俺比王菲小两个多月,比尔盖茨大俺整14年。俺家儿子今年上小学五年级,算是90后吧。

别看俺在网上象猴子般蹦蹦跳跳,嘻嘻哈哈,现实中却是个孤僻内向的糟老头子,见笑了。希望大家别嫌弃俺啊。

您以为俺那啥,那啥呢?


#29楼    回复  引用  查看    

2008-05-10 20:53 by 求知无傲      
童真类

#30楼    回复  引用  查看    

2008-05-10 21:08 by PerfectDesign      

#31楼    回复  引用  查看    

2008-05-10 21:13 by PerfectDesign      

主键是表的灵魂
但是不一定是主键都是最快的.
因此,聚集索引查找和定位数据的效率要比一般索引高出很多。
这句话不能太肯定,覆盖索引会减少IO.

#32楼    回复  引用  查看    

2008-05-10 21:30 by 伍迷      
且不谈这里的技术,就说博主的文采,就是非常值得肯定的。把王菲的故事拿来说主键,这是非常好的类比。学生上数据库原理,如果直接谈主键,谈记录唯一标识,谈一二三范式,那是多么的枯燥乏味。谈谈王菲,谈谈王靖雯,所有的学生都来劲了。赞一个!我得加油了,向你学习。

#33楼    回复  引用  查看    

2008-05-10 22:07 by 怪怪      
@李战
你可别这么叫我,这不是索命么~

倒不是谦虚, 虽说学无先后, 达者为师, 问题是我感兴趣的你未必感兴趣(比如FPS游戏), 你擅长的我却感兴趣, 也就是说只有我向你学习的份儿, 所以你是老师 :)

我还以为你是小年轻呢.., 完了, 压力又大了, 90后马上就追上来了, 老一代还不退下去, 没法活了 T _ T

#34楼    回复  引用  查看    

2008-05-10 23:01 by Anytao      
不管是十年前的,抑或是十年后的,将技术娓娓道来,如此精彩异常,值得学习啦:-)

#35楼    回复  引用  查看    

2008-05-11 02:55 by BZZ      
学习了

#36楼    回复  引用  查看    

2008-05-11 07:18 by 李涛      
好文章。赞一个!
李战老师文笔飞扬,联想恰当,姜还是老的辣啊。呵呵

#37楼    回复  引用  查看    

2008-05-11 10:29 by 笨笨的考拉熊      
对数据库主键的分析很好。

“按磁头读取顺序存放数据可以提高硬盘读取效率”,这是把磁盘读写想象成一个人看小说的样子了,以为顺序看肯定要比前后来回跳着看快。但实际上磁盘读写中,数据的存放顺序对读写速度毫无影响,有影响的是数据的存放区域。完全按照顺序建立主键,但存在了磁盘上不同磁道的扇区上,也要比乱序建立主键但连续存储在同一磁道的连续扇区上慢非常多。

#38楼    回复  引用  查看    

2008-05-11 11:55 by 金色海洋(jyk)      
主键讲得很好,我就不多说了,还是说一下硬盘吧。

我对硬件也不太了解,
再说硬盘,硬盘转数已经很快了,但是读取数据的速度却比较慢,就是说,当硬盘转一圈之后,磁头并不能够把这一圈的全部数据都读出来,只能读出来一部分扇区。

在转第二圈的时候,才能够读取下一部分扇区,要想把一圈的数据都读取出来,
可能需要硬盘转上好几圈。

这个也是好几年前的知识了,不知道现在还对不对了。


==============

再说程序上的,

我觉得想SQL Server 这样的程序只能管内存里的数据的存放方式,而根本就管不了数据在硬盘里的详细的存放方式,这个要交给操作系统来负责,可能操作系统也不管不了,要交给硬盘自己处理。

如果都是交给硬盘处理的话,那么硬盘根本就不会管你存放的是一棵大树,还是一个word文档。

所以说,我觉得“硬盘的表面,聚集索引的那颗B树将生长得很正很齐。”只能是一个美好的想象了。


#39楼    回复  引用  查看    

2008-05-11 13:54 by 一瓢      
如果用时间的话,是不是从毫秒级的转换成bigint?会不会存在一样的时间

#40楼    回复  引用  查看    

2008-05-11 20:11 by snowwolflibo      
非常欣赏楼主的文采
我也曾经坚持"必须以对象标识做主键"的观念。主键不应该具有任何业务含义。
理论上这是无可厚非的。

但从实用主义出发,有时也需要用“对象属性”做为主键。请分析以下案例:
1、需要在Sql management studio中通过SQL查询某业务表数据,该业务表存在一个外键,如果该外键对应的主键具体业务含义的话,如为用户名,我们可以很方便地知道该业务数据对应的用户是谁,否则必须用关联查询。当然,这只是一个方便。实用主义嘛。
2、如果要迁移数据库的数据,采用流水号主键模式,将会生成新的流水号,从而失去主从表对应关系。(当然你也可以将目标数据库主键的自增长属性去掉)。

所以说,还得具体问题具体分析,毕竟所有的软件思想都是为了提高软件的质量和降低成本,只要在业务上可以完全确保某些“对象属性”永远不变,用它做主键也未尝不可。有时这样会更方便嘛。

期待楼主的下部作品

#41楼    回复  引用  查看    

2008-05-11 20:46 by airwolf2026      
--引用--------------------------------------------------

--------------------------------------------------------
金色海洋,你那后面的硬盘假设有错误了...人脑确实处理不过哪么快的东西.但是机器却可以.也就是说硬盘转速快,还是可以一圈里面把数据都读出来的.相应的缓存等加大....
不然为啥现在的服务器硬盘15000转的为啥价格还是哪么贵?就知道了...而7200转...白菜价...

#42楼    回复  引用  查看    

2008-05-12 08:29 by cslar      
语不惊人死不休

#43楼    回复  引用  查看    

2008-05-12 08:54 by 木野狐(Neil Chen)      

#44楼    回复  引用    

2008-05-12 09:04 by testtt [未注册用户]
更改了发布时间,有什么重大更新?

#45楼    回复  引用  查看    

2008-05-12 09:06 by xjb      
这文章写的不错,深入浅出

#46楼    回复  引用  查看    

2008-05-12 09:14 by 玉开      
文笔真好,赞一个。

#47楼    回复  引用  查看    

2008-05-12 09:20 by 早班火车      
学习李老师的文笔!

#48楼    回复  引用  查看    

2008-05-12 09:24 by 王孟军!      
来晚了

#49楼    回复  引用  查看    

2008-05-12 09:28 by 超晨      
 崩溃了....
还以为是谁写的非技术贴,进来一看,原来又是李sir的大作啊,收藏回头再看

#50楼    回复  引用  查看    

2008-05-12 09:32 by 水果阿生      
谢谢楼主发这样的好文章,唯一一点问题是历史上略有出入,王菲出生以后叫做夏琳,15岁以后才改名叫做王菲

#51楼    回复  引用  查看    

2008-05-12 09:54 by good man      
有创新啊,学习了

#52楼    回复  引用  查看    

2008-05-12 09:57 by AlphaWu      
收藏一个先!

#53楼    回复  引用  查看    

2008-05-12 10:22 by 沧海月明      
博主的大作写得深入浅出,文笔不错,意思也说得很透彻。
我说一下我的拙见,不当之处,还请指教。

一、
主键都要整成聚集索引。
--------------------------------
这句话好象有问题,至少我的印象中我所看过的SQL教科书上并不如此说。
举一个例子,使用有规律订单号(比如时期+流水号)的订单数据,仍然以guid做为主键,但是使用订单号做为聚集索引,而主键作为非聚集索引, 在针对此表的操作,整体性能应该是较好的。


二、
对于大多数系统,确实如楼主所说,要使用一个在查询中使用最多的排序最多、最有序、重复值最少的字段做为聚合索引(我还是坚持认为聚合索引和主键是两回事),NEWSEQUENTIALID()字段做为聚合索引,其假想条件是数据的顺序=数据输入的顺序。如果这个条件不满足,意义也并不是特别地大。这个时候,如果有更理想的适合做聚合索引的字段,不用主键做聚合索引也未尝不可。

三、
对象数据库说,主键只能是对象标识!
至于对象属性是否唯一,那是由业务逻辑所决定的。如果业务逻辑规定订单号不能重复,就为订单号建一个唯一索引好了,但它不是主键。
--------------------------------------------------
订单的属性是它的客户,付款方式,....
订单号作为标识是没有问题的(当然,硬是要理解为属性亦可)。假设我们以自增整数当主键,其实我们是可以把这个整数当订单号,不再需要其它订单号字段,这不就形成了一个悖论?
虽然我个人仍倾向于使用guid字段做为主键,我觉得仅是编程方便和编程习惯的原因。

#54楼    回复  引用  查看    

2008-05-12 10:35 by 镜涛      
恩,主键这东东虽然业务逻辑可以决定,可是让它脱离业务逻辑而存在就可以避免很多问题了,毕竟业务逻辑是会变化的,说不定那天我们设置的主键就俨然有冲突或者变化了。这也是时间的问题,当这种情况出现时,哪么对整个系统将是一个灾难。如果设置为业务无关,哪么只要生成的合理,就能够在性能和语义上实现的很好。

#55楼    回复  引用  查看    

2008-05-12 10:35 by Tony Zhou      
good!

#56楼    回复  引用  查看    

2008-05-12 11:20 by 金色海洋(jyk)      
@airwolf2026
服务器硬盘15000转的为啥价格还是哪么贵?而7200转...白菜价...

可能就是差在磁头的读取和写入速度和缓存大小上了吧。

可能是高速读取的磁头成本很高的原因。

所以,2002年左右硬盘就已经 7200 转了,到现在还是7200 转。
为什么不快一点呢?马达不够吗?还是因为便宜的磁头处理不过来。

硬盘一圈(一个磁道)可以存放多少数据呢?我也不太清楚。那么这个数据和缓存来比较,比例是多少呢?

可能是读取一个扇区,缓存一个扇区,再读取扇区的时候,已经转过去好几个了。

#57楼    回复  引用  查看    

2008-05-12 11:26 by 麒麟.NET      
哇塞,看来楼主是王菲的同学啊!

#58楼    回复  引用  查看    

2008-05-12 11:28 by Clark Zheng      
写得很有意思

#59楼    回复  引用  查看    

2008-05-12 11:31 by 金色海洋(jyk)      
@沧海月明

1、主键都要整成聚集索引。
如果主键是 int 自增形式的,那么就没有什么争议了吧。
问题在于 如果主键是 guid 的时候怎么设置。

我还是会习惯性地 把主键设置为 聚集索引,当然这个只是个人习惯,因为我还没有使用 guid 形式的主键呢。


2、对象数据库说,主键只能是对象标识!

订单在数据库里面就是要有一个标识,而这个标识是要和逻辑没有任何关系的。

当然你可以把这个“标识”作为订单编号,但是反过来是不行的。


#60楼    回复  引用  查看    

2008-05-12 11:40 by 水平线      
不错...

#61楼 [楼主]   回复  引用  查看    

2008-05-12 11:56 by 李战      
@PerfectDesign
您说的对,如果一个索引覆盖了查询中用到的字段,而且这个索引也正好被查询用到,SQL执行引擎只需扫描该索引即可取得所需数据,无须再到表中取数据了。

我们总喜欢把聚集索引和表数据当成两个东西来看,其实,聚集索引的叶节点就是数据行,表数据就是聚集索引叶节点的集合。在SQL Server内部他们是一个整体,叫聚集表。如果你愿意把数据行当作聚集索引的一个节点,是否也可以理解为数据都是从聚集索引中取得呢?

一般索引的叶节点与聚集索引的叶节点不用,它也是一个一般的索引行,其中有指向数据表页的指针。当然,如果一般索引中的字段覆盖了查询字段,当然无须去表中去数。

俺是看SQL Server文档后才这么猜测的

#62楼 [楼主]   回复  引用  查看    

2008-05-12 11:58 by 李战      
@伍迷
谢谢老师夸奖,能不能给朵小红花?

@怪怪
现在CNDEV的大牛们都在显摆自己的孩子了,跟他们比,俺差远了。俺还要混饭吃啊,养老金不够。
以后还望你多指点。

@Anytao
您才是得道高人,到处是道啊。
收不收弟子?

#63楼 [楼主]   回复  引用  查看    

2008-05-12 11:59 by 李战      
@笨笨的考拉熊
倒着读你的回复:

------------------------------------------------
。多常非慢上区扇续连的道磁一同在储存续...
------------------------------------------------

不行,太慢,改成这样:

-----------------------------------------------------------
也要比乱序建立主键但连续存储在同一磁道的连续扇区上慢非常多。
但存在了磁盘上不同磁道的扇区上,
完全按照顺序建立主键,
...
-----------------------------------------------------------

每次要退一下,再顺着来一段,再退一下,再顺着来...

感觉还是顺着读好。

#64楼 [楼主]   回复  引用  查看    

2008-05-12 12:03 by 李战      
@金色海洋(jyk)
有空俺编个《硬盘故事》啥的,您等着。

#65楼 [楼主]   回复  引用  查看    

2008-05-12 12:04 by 李战      
@snowwolflibo
1.俺常用冗余来平衡性能,但主键还是比较固执。
2.俺那个数据库是多租户共享数据的,不能流水,只能随机。

俺一直在按你的指点做事:具体问题,具体分析。

俺会继续努力的。

#66楼 [楼主]   回复  引用  查看    

2008-05-12 12:06 by 李战      
@水果阿生
您比俺更喜欢王菲啊!

#67楼 [楼主]   回复  引用  查看    

2008-05-12 12:07 by 李战      
@沧海月明
一、如果你总按订单号查询和关联其他表,还整个额外的主键干啥呢?

二、的确,如果人们总按X方向查数据,你按Y方向存放数据就毫无意义了,更无须将Y当作主键了。

三、您遇到过客户订单号相同但卖的东西不同的问题吗?反正俺是吃尽了苦头。

#68楼    回复  引用  查看    

2008-05-12 12:54 by 金色海洋(jyk)      
@李战
我也一直想弄明白,数据在硬盘里的存放方式,但是好像挺难的,而且偏硬。
还是做好自己熟悉的吧。硬盘就交给操作系统吧。

其实我也想写主键的来着,但是真的没有你的文笔好,我感觉我们对主键的理解很像。

#69楼    回复  引用  查看    

2008-05-12 13:59 by 巫云      
怎么跟王菲战上了?

#70楼    回复  引用  查看    

2008-05-12 16:01 by hailibu      
古有托物言志,今有借人言物。妙哉妙哉!

#71楼    回复  引用    

2008-05-12 16:49 by zzticzh2 [未注册用户]
赞一个

#72楼    回复  引用  查看    

2008-05-12 16:57 by Kingthy      
李老师的文章果然文武飞扬啊。精彩啊!!!

#73楼    回复  引用  查看    

2008-05-12 21:42 by 水果阿生      
@李战
呵呵,是啊,算是菲迷吧,楼主大哥的文章写的非常好,支持您多发好文章

#74楼    回复  引用  查看    

2008-05-31 02:19 by 深蓝      
博主是一个会讲故事的人,也是一个有故事的人。
佩服博主。

#75楼    回复  引用  查看    

2008-06-04 15:25 by 狼Robot      
李老师有时间的话能不能说说如何生成主键?

#76楼    回复