论坛首页 Java企业应用论坛

[讨论]业务主键 Vs. 逻辑主键,到底哪个好?

浏览 85690 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-01-05  
robbin 写道

1、相对于业务主键带来的缺点,逻辑主键造成的性能下降究竟有多大,你可以测试一下,如果性能下降在5%以内,就不算性能下降。在Java编程中,new一个对象的时间开销是静态方法调用的100倍以上,那么是否我们就应该放弃面向对象编程,把所有东西都写成静态方法的那种函数式调用呢?相对于性能的轻微损失,整个数据库表的关系建立和维护更加重要的多。

以前在做VB的项目时候,为了这个做过一个简单的测试,在数据量不大情况下,对于性能的影响是微小的,随着数据量的增加,性能下降是比较明显的,

逻辑主键
records:1000  5000  10000
insert: 8.23  36.47 90.73
select: 0.75  3.40  8.03
update: 6.70  34.73 77.25
delete: 10.03 67.01 166.88

业务主键
records:1000  5000  10000
insert: 7.91  35.03 78.20
select: 0.62  3.01  7.17
update: 6.98  34.13 75.16
delete: 8.35  66.29 160.49


附件是test script(当时测试环境:SQL Server 2000 SP2,PIII 800, 512M SDRAM, Windows 2000 Personal SP1, 测试10次取平均,实际数据在3000~6000左右,所以只测试了最多10000 records的情况)。

在insert和select操作的时候,性能的差异还是蛮明显的(10%左右),在外键索引存在情况下的对于多表的insert,update复杂测试没有做,因为增加一个索引对于性能的影响太过复杂,很难跑复杂测试,用简单测试有一个概念就好了。

相对而言10%的性能损失还好,从数据库关系建立和维护方面,我认为业务主键的表之间关系要比逻辑主键更加清晰,维护数据的时候不需要多绕一个弯,比如数据库维护人员从数据库直接修改一个订单下面的订单具体条目的内容,用户提供给他的是一个订单号,他可以拿这个订单号直接查询出来结果进行修改,而不用先查这个订单号对应的逻辑主键,再拿逻辑主键值进行子项的查找。

robbin 写道

2、多一个字段而已,会对你造成多大的不便呢?

给一个对象增加逻辑主键属性是为了把Object持久化到关系型数据库中而出现的,在设计对象之间关系的时候,我们考虑的是业务之间的关系,而不是逻辑主键之间的关系,但是如果使用逻辑主键,那么到了DAO,我们必须多做一个mapping,这样的感觉不太好。


robbin 写道

3、一条记录可能有多个独一无二的属性,这个论据完全不能作为支持业务主键的理由。

我在实际项目当中还没有遇到某个对象有超过1个的unique属性,我想即便是真有这样的情况,也会有某个属性是primary,某个是second,通过分析primary unique attribute,我们可以更深入地了解用户需求。robbin可以举个你遇到的多个unique attribute object的实际例子么?
0 请登录后投票
   发表时间:2004-01-07  
用什么样的主建要根据具体项目来定,
如果你做的是一个小论坛或是其他什么小系统,
用什么主建都可以,怎么方便怎么来就可以了,

但是对于大系统,比如银行、电信就要小心规划了,
对于这样的大系统,即使你想用什么所谓的逻辑主建,银行的人
也不可能同意的,除了性能和空间的问题外,最主要的是,
在这种关键数据库中,每一个数据都是有其作用的,
涉及到数据仓库和数据挖掘等很多方面,
所以一定是业务主建。

其实,可以不客气的说,如果你有机会参与大项目的话,恐怕不会有
超过5%的表使用逻辑主建。
0 请登录后投票
   发表时间:2004-01-07  
你可以记住一句话:

数据就是数据
模型就是模型

你会不会有一天,为了方便,强迫每个人都要在脑袋上
顶一个牌子,上面写着自己名字以便于辨认呢

但是你会给动物栓上铭牌

这就是高级和低级的区别

所以,我觉得一定要养成使用业务主建的习惯和思维方式
除非你觉得自己永远不会有机会做“大项目”
0 请登录后投票
   发表时间:2004-01-07  
Quake Wang 写道
在实际项目中,我是大量的使用业务主键,可以说是业务主键的极端派,如果在业务上有属性能够独一无二的标识一个对象(即使是复合属性),我就一定会使用业务属性,主要是3点原因:
1. 性能:逻辑主键由于需要在数据库多增加一个业务无关的字段,而用户通常都是对于业务相关的字段进行查找(比如员工的工号,书本的ISBN No.),这样我们除了为逻辑主键加索引,还必须为这些业务字段加索引,这样数据库的性能就会下降。

2. 编程方便:对于用户操作而言,都是通过业务字段进行的,所以在这些情况下,如果使用逻辑主键的话,必须要多做一次映射转换的动作。

3. 能够更好的理解用户需求:因为我认为,在实际用例中,每个重要的业务对象实例总是有独一无二的属性的(流水记录性质的对象除外),为了能够抓出这些属性,必须要深入了解项目专业领域的知识,这样就能更好的理解用户需求,做一个好的项目。

想听听使用逻辑主键的人的看法。


同意!
在很多较大的项目里面,由于历史的原因(这些行业历经多年变化后的数据结构),数据库中每一个字段都有明确的含义。基本上是没有逻辑主键,也没有外键约束。
为什么这样呢?
很简单,实践中得来的,而且这些应用追求的是速度至上!这种设计模式也的确存在很多的问题,比如:修改某表的主键,会引起连锁反应。


如果是现代的中小型应用的话,还是采用流行的设计模式较好。
BTW:在oracle中,外键的支持是从哪一年才有的?
0 请登录后投票
   发表时间:2004-02-19  
谁能保证作为主键的业务字段客户不需要改动???如果此时还建立了外键,那又怎么办?
   不要指望能回答这个问题,我当初做设备管理系统的时候就碰到了,1.0版本就是用的业务主键,结果,市场的反馈迫使我们1.01版本对数据库进行了一个全范围重新的改动(全部采用sqlserver自动产生的GUID)。有人又会问,为什么不用自增长的ID,那么我又想问,如果数据不是在同一台服务器上录入,需要进行汇总的时候又怎么办?
    我不清楚hibernate的UUID是怎么生成的,但是W3C提供了一个开源的UUID生成方法:对象的hash码(6位)+时间戳(12)+模拟网卡ID(32)。
    后来的项目经验我又发现,如果完全采用UUID,那么对于业务数据也是蛮恐怖的,比如基础数据使用UUID,那么业务数据引用的必然是它,那么为了查询一条业务数据(Business Object),那么意味着我们需要访问多个表,或者join多个表的视图,这样性能的降低绝对是明显的。
    因为基础数据的修改是比较小的,甚至说可以限制其修改,那么考虑使用业务主键还是比较合适的。
0 请登录后投票
   发表时间:2004-02-19  
我还是没有搞清楚逻辑主键究竟是个什么概念,好象是 ORM 里的概念吧。不过因为我不用 Hibernate,所以只能从数据建模的角度来思考。在传统的数据建模领域里是没有逻辑主键的位置的,几乎所有的字段都有业务上的含义。比如办公公文分收文和发文两类,两类共有的一些字段放在公文表里,独有的字段放在收文表和发文表中。这样就有 3 张表:
公文表
收文表
发文表
后两张表可以说都是公文表的子类。通过一个唯一的公文 ID(我们叫做流水号)来关联。这个公文 ID 字段显然是业务主键。大部分针对公文的查询都是针对这个公文 ID 展开的。
robbin 写道
最后要说明的是,即使采用sequence做主键,在进行数据迁移和合并的时候,也没有任何理由直接进行行记录的导入。例如如下的做法:

insert into main_table select * from sub_table;

你怎么能够保证分公司的数据表里面不存在对于总公司来说是非法的数据呢?即使你用业务主键,你难道就完全能够避免主键重复的问题?对于此类问题,数据的迁移必须先经过一到validate,才能导入。而在你做validate的时候,你就应该过滤掉主键,采用总公司数据库的sequence。

采用 sequence 是没有问题的。可以在表中再加一个“单位编号”的字段。由 sequence 和这个单位编号字段合在一起做 PK。这样即使你做合并也不需要做什么过滤了,因为总部和分支机构的单位编号是不同的。还有一种方法是划分开 sequence 的使用范围,比如
总部:1 ~ 1000000000
分支1:1000000001 ~ 2000000000
分之2:2000000001 ~ 3000000000
这个比这位老兄说的方法要好一些:
引用
这个倒是可以用>1的步长的SEQUENCE来解决
比如:
分公司1 的SEQ是1-6-11-。。。
分公司2 的SEQ是2-7-12-。。。
分公司3 的SEQ是3-8-13-。。。
分公司4 的SEQ是4-9-14-。。。
分公司5 的SEQ是5-10-15-。。。

步长=公司个数

你说,那么万一不够用了怎么办?好办,那时候已经 5 年以后了,5 年后重新给他们做一套系统好了。

不好意思,如果是这个定义的话:
引用
1业务主键(natrual key),有意义的字段。
2逻辑主键(surrogate key),无意义的字段,即自增长字段,即SEQUENCE。

(好象和 ORM 没有任何关系)

那么我赞成使用逻辑主键。业务主键很多时候不是 int 类型,做 PK 的性能会很差的。为什么大项目就很少用逻辑主键?说来听听,有人证明过吗?我们前年做的一个项目应该算是大项目了。
0 请登录后投票
   发表时间:2004-02-19  
折中一下吧,用业务主键或者逻辑主键不是绝对的。如果能用业务主键就尽量多用业务主键。如果没有唯一的业务属性可以做主键,或者这个属性字段类型不适合做主键,就建立逻辑主键。
在 OLAP 中,常用维表--事实表的星型结构,维表中一般都是 int 类型的字段,指向事实表中的主键。如果一定要采用业务主键,并且这个唯一的业务属性不是 int 而是 string 类型(假如人名是唯一的话,“张三”对应的 ID 是 34567,你说这个 34567 是逻辑主键还是业务主键?当然是逻辑主键。你还要至少建两个索引),直接把这个 string 字段放进维表,你说哪个性能会更好(是多建了一个索引影响性能呢?还是直接在 string 上做索引影响性能?)?所以“业务主键肯定比逻辑主键性能好”的说法好象没有什么说服力。
0 请登录后投票
   发表时间:2004-02-20  
以我的理解,要解决这个问题,首先要搞清楚主键是干什么的。

键是数据库实现“完整性约束”的手段。主键是用来标识一个实体的,就像对象引用(reference)。

数据库不区分逻辑数据还是业务数据。对它来说,只有变动的数据和不变的数据。

作为一个实体标识,毫无疑问,这必须是一个唯一不变的数据。对人来说,业务数据只是对人有意义的数据,它可能变,可能不变。只是现实中,对人有意义而又确定不变而且每个实体都必定会有的数据好像很少。身份证号,美国的社会福利保险号等,可以满足这些条件,但这些属于比较个人的数据,不可能每个系统都要求提供。

所以,我的看法,如果有满足以上条件的业务数据,那用这个业务数据作主键,这样程序也好写一点。如果不满足以上条件,那用逻辑主键,不能冒违犯“完整性约束”的险。我不赞成使用复合主键。

p.s:实际上,好像很难碰到满足使用业务主键条件的场合,还是使用逻辑主键多吧
0 请登录后投票
   发表时间:2004-02-20  
dlee兄,在对象建模当然也有逻辑主键,只是隐含了而已。一个逻辑主键的意思就是对象实例的唯一性,简单地说,人的身份证号码是业务主键,而具体的人则隐含着一个逻辑主键,也就是说不管出现什么样的情况,dlee还是dlee(当然不要跟我讲克隆, )。
   在做建模的时候我们当然不会考虑这个问题,因为它和实例有关系,和对象(指的是建模层的对象,不是类的实例)是没有关系的,所以在做建模时是不会考虑的。这也就是我们为什么叫它逻辑主键的意思(表明它在现实领域是看不到,但是是逻辑存在的)。
    再将上面的例子反应到计算机领域,如果拿业务主键身份证作为人的唯一判断,那么就出现了一个问题,所有引用改身份证的地方,如果出现了身份证的改动(客观存在,谁的身份证都升了位),那么将带来很大的麻烦(这种麻烦在现实领域好处理,并且有足够的时间进行处理,但是在计算机领域就不简单了)。
    我的建议:对于业务数据,是需要采用逻辑主键的,对于基础数据,基于多方面考虑,是可以采用业务主键的。
0 请登录后投票
   发表时间:2004-02-21  
to 凤舞凰扬:
你说得很对。实际上在我看到这个线索时还根本没有搞清楚逻辑主键和业务主键的区别,最后才明白了(那个写定义的人把逻辑主键与采用 sequence 的主键搞混了,变成了是否要使用 sequence 的讨论。这些人的交流能力实在是够差的)。我的感觉是这好象不是一个非常值得讨论的问题。因为我在做数据库设计的时候几乎很少考虑其中的区别,总是首先考虑实现(编程)的代价,首先使用业务主键(当然在适合的属性存在的情况下),如果觉得逻辑主键更合适,可以很大程度减少编码量,我就会使用逻辑主键。至于性能,这个不是设计人员首先应该考虑的问题,如何提高开发效率,尽快交付可工作的系统才是他更应该考虑的。

这样的讨论似乎很深入,但是因为离开了具体的语境,所以谁也很难说服谁,说到底还是空谈。

比如我说的那个公文表、发文表、收文表的含有继承关系的情况下,假设“公文字号”是一个唯一属性,但是这个属性是 string 型。那么我肯定要增加一个 ID 字段(事实上我们几乎每张表都有这样一个 ID 字段)来做主键,这个字段就是一个逻辑主键。有人能告诉我这个时候把“公文字号”当作主键有什么好处吗?

还有就是你说的那个身份证号码变更的例子,这种情况下,dlee 还是 dlee,而不能因为改变了身份证号码,dlee 就变成了另外的一个人。唯一的属性如果是可变的,也不适合作为主键。还有一个做法是,如果有些属性有通用性(很多地方都用到),可以把它当作代码,单独设置一张代码表。
代码类别
代码 ID
代码名称

然后在这个属性中保存代码 ID。可以同时增加一个代码名称的冗余字段,保存的是当时代码表中对应的代码名称。以后即使代码名称改掉了,只要代码 ID 稳定不变,就不会影响到对这个属性所做的统计。在做数据库设计的时候你总要假设一些东西是不变的吧?否则你如何做设计呢?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics