zoukankan      html  css  js  c++  java
  • about数据库表的物理主键与逻辑主键

    **about业务主键和逻辑主键

    1业务主键(natrual key),有意义的字段。 对前端可见
    2逻辑主键(surrogate key),无意义的字段,即自增长字段,即identity。这其中还有一个选择GUID。

    如果有业务主键,数据唯一性就能得到保证,逻辑主键存在的意义?
    尽量不要用GUID作 PRIMARY KEY.
    PRIMARY KEY可以用多列,不一定非要使用以列

    以下是一些反对用逻辑主键的理由,
    --
    1.They 're not standard SQL. Most products have it but there 's no consistent implementation.
    2.They can 't be updated. This violates the relational data model (not fatal, but not good either). Duplicates can be accidentally inserted (fatal).
    3.They only create numeric values. GUID/NewID() are also numeric only, and are hard to read.
    4.Numeric values are not meaningful in many tables, and adding them complicates relationships between other tables.
    --
    Problems such as supporting replication, merging, and a providing a limited number of unique values for a heavy transactional systems
    --
    IDENTITY字段不要作为表的主键与其它表关联,这将会影响到该表的数据迁移。
    --

    *viewPoint01

    既然有业务主键,数据唯一性就能得到保证了。 
    那么你的逻辑主键的存在的意义或者是目的是什么呢? 
    逻辑主键的存在的意义在于当业务主键是多个字段组合的和/或业务主键的数据类型不便于比较的时候,用业务主键作为聚集索引影响性能,这个时候加一个冗余的数字型的逻辑主键显得很必要,也会方便编程。 
    加逻辑主键应该主要是从性能考虑

     

    *viewPoint02

    我有不同的观点: 
    我认为业务主键作为聚集索引反而更有利,因为在大数量的业务操作中,操作员能记住并交流的的就只有那么几个有限的数据:业务单据号、物品编号(名称)、客户编号(名称)。他们在查询的时候最多就是这些,最多还要加上日期时间。数据库查询的压力都来自他们。所以应该说好处是比较大的。 
    说到这里,我还要说个题外话。除开WEB的设计方式来讲,企业的关键数据是不允许出现很多人在这里问的要怎么删除重复数据的情况的。不是完全不能,只是回给大家带来很多困扰,影响后面的流程。 
    话说回来,一个逻辑主键与一业务主键共存的数据集用哪个来与外界联系?都可以! 
    那么逻辑主键不是流水号,每个表都有1和10,困扰也很大。

     

    *viewPoint03

    其实我觉得比较好的是两种方法都用。 

    最好是业务主键做为聚集索引,identity做唯一索引。 

    identity并不适合做为聚集索引,因为   identity若做为聚集索引,性能不好(虽然我自己也没感觉到)。用identity做为聚集索引,会造成局部热点,也是就某个存储区域过热,这样会造成大量的操作都在相邻的存储区域进行,会对性能造成影响。理论上我觉得是很有道理的,不过实际上我没有什么体会。 

    业务主键做为聚集索引,会对查询性能有很多好处。 

     

     

    *viewPoint04

    “既然有业务主键,数据唯一性就能得到保证了” 

    引入   Identity,GUID   的原因就是业务主键的唯一性很难保证,因为自然主键来自实际业务,往往是人工编制而成,其唯一性不容易得到保证。 

    Identity   作主键,除了唯一性可以保证以外,还可以提高索引的速度,因为对索引而言,   INT   数据类型的   Identity   的速度是最快的。

    但我也很喜欢用identity(或无意义的关键字),因为在做程序时,用一个integer做为参数就可以了,不必用几个参数,而且也方便移值给其它表或其它系统使用。用identity也有它的好处。 

    至于复制,如果要考虑分发复制的话,我觉得不必怪罪到identity头上,你大可以用业务主键来做复制,为什么要用identity?

     

    *viewPoint05

    其实我在WEB开发的时候就是会用Identity做主键的,感觉比较爽。 
    但是换过来我在做企业管理软件的时候就是采用‘自定义’业务主键了。所谓自定义就是在一开始由用户定义一个格式比如,长度,打头字母,起始编号等,当然这个编号最后还是依据用户定义自增长的。所以从来都没有出现过主键重复过:) 
    而且用户也乐意使用这样的业务主键。

     

    *viewPoint06

    对老外的话(他是逻辑主键的反对方),我觉的他说的可能是这种情况(当然各位可能已经理解了):   
    当手工合并两张表时,这两张表的IDENTITY字段很可能都是1,2,3,4,5........,这样问题出现了,主键不唯一,首先你需要重建IDENTITY来保证主键唯一,另外引用他们的外键全得修改。   

    坛子里我想有很多人都认为该用逻辑主键,但象上面这种情况各位是否考虑过?处理过? 
    更棘手的问题: 
    几个分公司的全部数据(其中有很多父子表),定时发送到总公司。分公司、总公司表结构一样,只要用到IDENTITY,真的很麻烦了...

     

    *viewPoint07

    我以前经常处理这样的表,只要头脑清楚就行了。麻烦是麻烦了一点,但谁会天天去倒数据呢? 

    总公司和分公司的情况,你可以另加一个字段branchID,用branchID   +   ID做为唯一键。 

    用identity的一些好处也是有的,比如做三层结构的设计时,不同的单据共用相同的对象模型或公用函数,但不同的单据的业务关键字都不一样,就会很不好做统一的接口,用identity则所有的单据都只用ID做为参数就可以了,问题迎刃而解。 

    所以用不用,可以按实际情况决定取舍。

    A:icevi(按钮工厂)   前面不是说了一个方法了吗,就是加上一个表示分支的branchID。用branchID+IDENTITY组合成合并表的主键。

    *viewPoint08

    用identity确实有些问题,首先是合并复制问题。 
    MS给出3种方案: 
    1Automatic   Identity   Range   Handling。 
    2Manual   Identity   Range   Handling。即NOT   FOR   REPLICATION   。 
    3Using   Other   Columns   as   Primary   Keys。这第3种方案即icevi(按钮工厂)所说的方法。 

    我现在的问题:这3种方法中,各位认为第3种方案是最好的吗?只是觉得有些麻烦,每个有INDENTITY的表都得有个BranchID;如果我每个表都有IDENTITY,编码时,每一次增,删,改,查都要跟上BranchID,噩梦? 

    因为公司业务经常是无法预见的,开发时还是一个公司,两年后,可能全国各地好多子公司,按着这种思路,开发之初就应该加BranchID。

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    定义(部分定义来源于 SQL Server 联机丛书):

    主键(PRIMARY KEY):表通常具有包含唯一标识表中每一行的值的一列或一组列。这样的一列或多列称为表的主键 (PK),用于强制表的实体完整性。

    外键(FOREIGN KEY):外键 (FK) 是用于建立和加强两个表数据之间的链接的一列或多列。在外键引用中,当一个表的列被引用作为另一个表的主键值的列时,就在两表之间创建了链接。这个列就成为第二个表的外键。

    聚集索引:聚集索引基于数据行的键值在表内排序和存储这些数据行。每个表只能有一个聚集索引,因为数据行本身只能按一个顺序存储。

    非聚集索引:非聚集索引包含索引键值和指向表数据存储位置的行定位器。可以对表或索引视图创建多个非聚集索引。通常,设计非聚集索引是为改善经常使用的、没有建立聚集索引的查询的性能。

    自动编号列和标识符列:对于每个表,均可创建一个包含系统生成的序号值的标识符列,该序号值以唯一方式标识表中的每一行。

    业务主键(自然主键):在数据库表中把具有业务逻辑含义的字段作为主键,称为“自然主键(Natural Key)”。

    逻辑主键(代理主键):在数据库表中采用一个与当前表中逻辑信息无关的字段作为其主键,称为“代理主键”。

    复合主键(联合主键):通过两个或者多个字段的组合作为主键。

    原理分析:

    使用逻辑主键的主要原因是,业务主键一旦改变则系统中关联该主键的部分的修改将会是不可避免的,并且引用越多改动越大。而使用逻辑主键则只需要修改相应的业务主键相关的业务逻辑即可,减少了因为业务主键相关改变对系统的影响范围。业务逻辑的改变是不可避免的,因为“永远不变的是变化”,没有任何一个公司是一成不变的,没有任何一个业务是永远不变的。最典型的例子就是身份证升位和驾驶执照号换用身份证号的业务变更。而且现实中也确实出现了身份证号码重复的情况,这样如果用身份证号码作为主键也带来了难以处理的情况。当然应对改变,可以有很多解决方案,方案之一是做一新系统与时俱进,这对软件公司来说确实是件好事。

    使用逻辑主键的另外一个原因是,业务主键过大,不利于传输、处理和存储。我认为一般如果业务主键超过8字节就应该考虑使用逻辑主键了,因为int是4字节的,bigint是8字节的,而业务主键一般是字符串,同样是 8 字节的 bigint 和 8 字节的字符串在传输和处理上自然是 bigint 效率更高一些。想象一下 code == "12345678" 和 id == 12345678 的汇编码的不同就知道了。当然逻辑主键不一定是 int 或者 bigint ,而业务主键也不一定是字符串也可以是 int 或 datetime 等类型,同时传输的也不一定就是主键,这个就要具体分析了,但是原理类似,这里只是讨论通常情况。同时如果其他表需要引用该主键的话,也需要存储该主键,那么这个存储空间的开销也是不一样的。而且这些表的这个引用字段通常就是外键,或者通常也会建索引方便查找,这样也会造成存储空间的开销的不同,这也是需要具体分析的。

    使用逻辑主键的再一个原因是,使用 int 或者 bigint 作为外键进行联接查询,性能会比以字符串作为外键进行联接查询快。原理和上面的类似,这里不再重复。

    使用逻辑主键的再一个原因是,存在用户或维护人员误录入数据到业务主键中的问题。例如错把 RMB 录入为 RXB ,相关的引用都是引用了错误的数据,一旦需要修改则非常麻烦。如果使用逻辑主键则问题很好解决,如果使用业务主键则会影响到其他表的外键数据,当然也可以通过级联更新方式解决,但是不是所有都能级联得了的。

    使用业务主键的主要原因是,增加逻辑主键就是增加了一个业务无关的字段,而用户通常都是对于业务相关的字段进行查找(比如员工的工号,书本的 ISBN No. ),这样我们除了为逻辑主键加索引,还必须为这些业务字段加索引,这样数据库的性能就会下降,而且也增加了存储空间的开销。所以对于业务上确实不常改变的基础数据而言,使用业务主键不失是一个比较好的选择。另一方面,对于基础数据而言,一般的增、删、改都比较少,所以这部分的开销也不会太多,而如果这时候对于业务逻辑的改变有担忧的话,也是可以考虑使用逻辑主键的,这就需要具体问题具体分析了。

    使用业务主键的另外一个原因是,对于用户操作而言,都是通过业务字段进行的,所以在这些情况下,如果使用逻辑主键的话,必须要多做一次映射转换的动作。我认为这种担心是多余的,直接使用业务主键查询就能得到结果,根本不用管逻辑主键,除非业务主键本身就不唯一。另外,如果在设计的时候就考虑使用逻辑主键的话,编码的时候也是会以主键为主进行处理的,在系统内部传输、处理和存储都是相同的主键,不存在转换问题。除非现有系统是使用业务主键,要把现有系统改成使用逻辑主键,这种情况才会存在转换问题。暂时没有想到还有什么场景是存在这样的转换的。

    使用业务主键的再一个原因是,对于银行系统而言安全性比性能更加重要,这时候就会考虑使用业务主键,既可以作为主键也可以作为冗余数据,避免因为使用逻辑主键带来的关联丢失问题。如果由于某种原因导致主表和子表关联关系丢失的话,银行可是会面临无法挽回的损失的。为了杜绝这种情况的发生,业务主键需要在重要的表中有冗余存在,这种情况最好的处理方式就是直接使用业务主键了。例如身份证号、存折号、卡号等。所以通常银行系统都要求使用业务主键,这个需求并不是出于性能的考虑而是出于安全性的考虑。

    使用复合主键的主要原因和使用业务主键是相关的,通常业务主键只使用一个字段不能解决问题,那就只能使用多个字段了。例如使用姓名字段不够用了,再加个生日字段。这种使用复合主键方式效率非常低,主要原因和上面对于较大的业务主键的情况类似。另外如果其他表要与该表关联则需要引用复合主键的所有字段,这就不单纯是性能问题了,还有存储空间的问题了,当然你也可以认为这是合理的数据冗余,方便查询,但是感觉有点得不偿失。

    使用复合主键的另外一个原因是,对于关系表来说必须关联两个实体表的主键,才能表示它们之间的关系,那么可以把这两个主键联合组成复合主键即可。如果两个实体存在多个关系,可以再加一个顺序字段联合组成复合主键,但是这样就会引入业务主键的弊端。当然也可以另外对这个关系表添加一个逻辑主键,避免了业务主键的弊端,同时也方便其他表对它的引用。

    综合来说,网上大多数人是倾向于用逻辑主键的,而对于实体表用复合主键方式的应该没有多少人认同。支持业务主键的人通常有种误解,认为逻辑主键必须对用户来说有意义,其实逻辑主键只是系统内部使用的,对用户来说是无需知道的。

    结论或推论:

    1、尽量避免使用业务主键,尽量使用逻辑主键。

    2、如果要使用业务主键必须保证业务主键相关的业务逻辑改变的概率为0,并且业务主键不太大,并且业务主键不能交由用户修改。

    3、除关系表外,尽量不使用复合主键。

    使用逻辑主键的最佳实践指南:

    1、足够用就好。系统使用的生命周期以100年为限,逻辑主键数据类型采用下表规则,如果不确定则使用int类型。

    数据量 数据类型 数据大小 生成频率 备注
    < 128 tinyint 1 字节 1条/年 频率过低,不太靠谱,不建议采用
    < 3 万 smallint 2 字节 27条/月 频率较低,慎用
    < 21 亿 int 4 字节 40条/分钟 能满足大部分情况
    < 922 亿亿 bigint 8 字节 292万条/毫秒 能满足绝大部分情况 
    >= 922 亿亿 uniqueidentifier 16 字节

    100亿用户同时每毫秒生成10亿条,可以连续生成10亿年

    可用于分布式、高并发的应用

    2、一般采用自增长方式或NewID()方式。

    3、主键字段名称一般采用“表名ID”方式,方便识别和表联接。

    4、如果表存在分布式应用,则可以考虑采用不同起始值,相同步长方式自增。例如有3个部署在不同地方的库,则可以如下设计:

    起始值 步长
    1 10
    2 10
    3 10

    步长统一设置10是为了方便日后扩展,这样不同库之间也能保持主键唯一性了,也方便合并。

    5、如果存在高并发性需求或数据表迁移需求,可以考虑使用uniqueidentifier类型,并使用NewID()函数。

    6、可以考虑对业务主键建立唯一性索引,以实现业务主键唯一性的业务需求。

    7、如果需要考虑业务主键的性能需求,可以把业务主键建立聚集索引,而逻辑主键只建立主键约束和非聚集索引即可。

    8、关系表可以考虑采用复合主键方式,复合主键不用于实体表。

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    *viewPoint09

     1、从纯数据库设计的角度来看: 
    使用业务主键是自然的选择,但如果业务发生变化引起业务主键的变化怎么办?(xiaoyu举的例子),所以引入逻辑主键id(当然不是每张表都要),浪费空间之说我认为是没有必要考虑的,存储技术的发展能容忍这点小浪费。而且单一的id在多表关联时,显然比复合主键有更好的性能。 
    2、从WEB应用开发的角度来看: 
    用逻辑主键你只需要在页面和后台之间传递一个单一的参数,简化了开发工作,而且有较高的查询效率。 
    3、从Hibernate应用的角度来说: 
    复合主键需要更多的处理,性能不高,所以Hibernate不提倡用复合主键

     

     

  • 相关阅读:
    CSP2020 游记
    React中useLayoutEffect和useEffect的区别
    Vue前后端分离跨域踩坑
    Python 正则将link 和 script 处理为 Django static形式
    BootStrap4
    单例模式
    匈牙利算法——求二部图的最大匹配的匹配数
    抽象工厂模式
    工厂方法模式
    JDK配置步骤
  • 原文地址:https://www.cnblogs.com/LIS2011/p/2748001.html
Copyright © 2011-2022 走看看