业务主键和代理主键的比较

本文对比了业务主键与代理主键的优缺点,从业务需求、系统性能等方面进行分析,帮助开发者选择合适的主键策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自 :http://hi.baidu.com/zbzb/blog/item/f2e61a95d78ba90a7af480b0.html

2007年08月06日 星期一 上午 09:54

业务主键和代理主键的比较

这是个老话题,人们在做数据库设计时常常按照个人偏好或一些技术文档上的简单推荐来选择它们二者,但是世上没有放之四海而皆准的东西,所以它们两者一定会相对更适合应用于不同的环境。下面对两者各种的优势加以说明。

优势和不足

下文中提到的“业务编号”是可以唯一确定一条记录的有意义的内容,也就是业务主键。

业务主键优势

业务主键

代理主键

1

具有更好的检索性能。同时基于主键检索更加方便。

因为使用业务主键来查询关联表时,可以减少连接主表的几率,减小I/O量。

连接开销只能通过增加冗余来解决,即在子表中保存主表的“业务编号”。但是这样做的话,使用代理主键的意义就失去了很多。因为,它会引起使用冗余数据后存在的所有问题,可是又没有解决代理主键的许多不足。

2

更好可读和便于理解。

3

合并相同业务实体更加容易(不同实体的主键编码不同)。

举例:两个分公司的订单,每月汇入总公司的数据库表。如果前期已经分公司订单编号区分开了,则合并数据就可以直接COPY。

如果代理主键是numeric类型的,那么前期必须定义各个分公司的订单表及子表使用不同的范围取值的代理主键。这虽然无需修改应用,但是增加了系统实施和后期维护的工作。如果代理主键是UUID或GUID这样的对象,那么则不需要。

业务主键的这一优势只限前期对不同分公司的业务主键的编码做了区分,如果没有,那么做数据合并时将更加麻烦。相反,代理主键此时反倒更具优势,因为数据合并时,只需要过滤分公司的主键而使用总公司的主键,无需修改业务编号,例如:单号、产品号。

4

数据迁移更加容易。

对于numeric类型的代理主键,在不同数据库间迁移数据时,需要重新计算被迁移实体的代理主键

5

复杂性低和存储量少。

使用代理主键需要增加格外的索引,从而导致增加了insert的负担和需要更多的存储。

6

代理主键不是SQL的标准,不同数据库系统实现不一。因为不同数据库系统在此实现不一,导致应用跨数据库平台不方便。

可以在系统中使用ORM框架,例如:hibernate或EJB。虽然现在的ORM框架在不断完善,但是也依然有许多限制。

7

代理主键无法保证相同业务的数据被重复插入。因为代理主键是无意义的,有意义的是业务编号,当使用代理主键时,业务编号就可能重复,这破坏了关系模型。

可以为使用代理主键的实体中的“业务编号”字段添加唯一索引。

8

代理主键不能被改变。

代理主键优势

代理主键

业务主键

1

在业务发生变化时,适应性更强一些。

举例:产品编码规则发生变化。此时,产品编码不是主键,所以只需要按照新的编码规则更改产品实体表内的“业务编号”,而不会影响到其他实体。

因为产品编码是产品实体的主键,所以产品编码内容在其他实体中会作为外键,此时需要更多的工作,才能完成升级。如果那些实体已经备份到外面了,那还需要导入数据修改等工作。

当然,业务发生变化带来的升级不会仅仅update几个字段就解决的,它必然带来更多系统的考虑,比如:兼容性。

2

保证系统的一致性和系统可操作型。

举例:订单表和订单子表,订单使用“订单号”作为业务主键,订单子表使用“订单号”和序列号作为复合的业务主键。一旦订单子表的记录被其他单据引用,希望调整订单子表记录的序列号的先后次序就很麻烦了。

可以为订单子表添加一些“排序次序号”之类的字段,但是这样的字段与序列号语义上是一样的。

3

数据表连接和更新的性能更高。

举例:订单表和订单子表,订单使用“订单号”作为业务主键,订单子表使用“订单号”和序列号作为复合的业务主键。

通过业务主键做关联时,连接的开销将比代理主键大很多,而且随着业务主键(CHAR型)位数增加,性能会不断降低。相反代理主键的连接开销将相对固定,只与数据量有关,而与业务约束有关。

另外业务主键时常是复合主键,这进一步拉低了连接和更新的性能。例如更新订单子表一个记录,需要使用订单的业务编号和子项的序列号作为where条件。

4

存在主从关系时候,业务主键更新不方便。

举例:在实际应用环境中,用户也许会出错,录入错误的单号、产品号等,等发现时这些业务主键已经被多处地方使用作为外键,这时需要修改这些错误信息就会增加工作量。

5

适合与现在JAVA的ORM框架配合使用,例如Hibernate

Hibernate的数据对象中的OID对象可以直接映射为代理主键,同时OID的新建也可以使用数据库的原生机制,从而可以被hibernate有效管理。

6

在并发环境下,更容易实现唯一的ID

因为在代理主键是numeric类型时,可以直接使用数据库的auto increment类型的字段或依靠sequence对象产生内容。

7

系统占用的存储空间更省。

例如:CHAR(7)的业务主键和INT型的代理主键。在百万行的数量下,代理主键上的索引容量=业务主键索引存量×80%,代理主键字段存储容量=业务主键索引存储容量/2。

虽然从单表看,因为多了代理主键,所以需要更多的存储开销(代理主键字段和PK索引),但是从系统角度看,其他实体的numeric的外键将比CHAR类型的外键节省更多的空间。

总结

从关系数据库理论角度说,主键是由设计者选定的在同一实体集中区别不同实体的候选码。从这个角度看,主键的选择是客观中带有主观的,那么用有意义的实体属性还是一个无意义GUID去标识实体本没有差别,毕竟世间万物都可以给一个全世界唯一的GUID去唯一限定,只要人愿意这么做。因此,对业务主键和代理主键的取舍,更多的是需要从系统、应用环境、实体属性与关系、开发效率、系统性能和维护成本等多方面去思考

<think>好的,用户之前让我介绍了数据库的类型,现在他们想了解主键外键。我需要先回顾一下之前的内容,确保这次回答能衔接上。主键外键在关系型数据库中非常重要,用户可能是在学习数据库设计或者准备考试,需要明确这两个概念的区别用途。 首先,主键的定义特点。主键是唯一标识表中每条记录的字段,必须唯一且非空。我需要举一些例子,比如用学号作为主键,或者自增ID。还要说明主键可以有单字段或者复合主键,比如订单号加产品号的组合。 然后外键,外键用于建立表之间的关系,它指向另一个表的主键。需要强调外键的作用是保持数据的一致性完整性,比如在订单表中,用户ID作为外键指向用户表的主键,确保订单属于存在的用户。还要提到外键约束的行为,比如级联更新或删除,以及可能出现的性能问题。 用户可能的深层需求是理解如何设计数据库表结构,或者解决数据完整性问题。要确保解释清楚主键外键在实际中的应用场景,比如避免重复数据,维护表之间的关系。可能需要提到不使用外键的情况,比如在高并发场景下,可能用应用层逻辑代替外键约束,但这样需要自己处理一致性。 还要注意使用之前的格式,分点说明,保持结构清晰,代码示例用正确的SQL语法。比如创建表时定义主键外键的语句,以及插入数据时的错误情况。这样用户能直观看到语法应用方式。 最后,总结主键外键的重要性,以及在不同场景下的使用建议。确保回答真实可靠,结构清晰,帮助用户逐步理解这两个概念。</think>### 主键与外键详解 主键(**Primary Key**)外键(**Foreign Key**)是关系型数据库中用于维护数据完整性表间关联的核心机制。以下是两者的定义、作用及实际应用场景: --- #### 一、主键(Primary Key) ##### **1. 定义** - 主键是表中**唯一标识每一行数据**的字段或字段组合,确保每条记录的唯一性。 - **核心特性**: - **唯一性**:主键值不可重复。 - **非空性**:主键字段不能为 `NULL`。 - **稳定性**:主键值通常不随业务逻辑变化而修改。 ##### **2. 作用** - **唯一标识记录**:例如,用户表中的 `user_id` 唯一确定一个用户。 - **加速数据检索**:主键默认生成唯一索引(如 `PRIMARY INDEX`),提升查询效率。 - **支持表间关联**:作为外键的引用目标,建立表与表的关系。 ##### **3. 设计规则** - **单字段主键**:常见于自增整数(如 `id INT AUTO_INCREMENT`)或业务唯一标识(如身份证号)。 - **复合主键**:多个字段联合唯一(如订单表中的 `order_id` + `product_id`)。 - **示例**: ```sql CREATE TABLE users ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE ); ``` --- #### 二、外键(Foreign Key) ##### **1. 定义** - 外键是表中指向另一个表主键的字段,用于**建立表与表之间的关联关系**。 - **核心特性**: - **引用完整性**:外键值必须在被引用表的主键中存在(除非允许 `NULL`)。 - **级联操作**:可定义删除或更新时的联动行为(如 `CASCADE`、`SET NULL`)。 ##### **2. 作用** - **维护数据一致性**:例如,订单表中的 `user_id` 必须指向有效的用户,避免“孤儿订单”。 - **实现数据关联查询**:通过 `JOIN` 操作跨表获取关联数据(如查询用户的所有订单)。 ##### **3. 设计规则** - **外键约束**:需显式定义关联的主键字段。 - **示例**: ```sql CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE -- 用户删除时,其订单自动删除 ); ``` --- #### 三、主键与外键的对比 | **特性** | **主键** | **外键** | |------------------|-----------------------------------|-----------------------------------| | **唯一性** | 必须唯一 | 可重复(除非联合唯一约束) | | **空值** | 禁止 `NULL` | 允许 `NULL`(根据业务需求) | | **作用范围** | 单表内标识记录 | 跨表建立关联关系 | | **索引** | 自动创建唯一索引 | 可手动创建索引(非必须) | --- #### 四、实际应用场景 ##### **1. 主键场景** - **用户表**:用 `user_id` 唯一标识用户。 - **商品表**:用 `product_code` 确保商品唯一性。 - **日志表**:使用自增主键 `log_id` 快速插入检索。 ##### **2. 外键场景** - **订单与用户**:订单表中的 `user_id` 外键关联用户表。 - **评论与文章**:评论表的 `article_id` 外键指向文章表的主键。 - **部门与员工**:员工表的 `department_id` 外键关联部门表。 --- #### 五、注意事项 1. **外键性能**: - 外键约束可能影响写入性能(需检查引用完整性),高并发场景可权衡是否使用。 - 可通过应用层逻辑替代外键(但需自行保证数据一致性)。 2. **主键选择**: - 避免使用业务含义字段(如身份证号)作为主键,优先选择代理键(如自增 `id`)。 - 复合主键需谨慎设计,可能增加索引复杂度。 3. **级联操作风险**: - `ON DELETE CASCADE` 可能导致数据意外删除,需结合业务需求配置。 --- #### 六、总结 - **主键**是数据的“身份证”,**外键**是表间的“桥梁”。 - 合理使用主键外键,可有效保障数据一致性关联查询效率。 - 在分布式或高吞吐场景中,可能需弱化外键约束,改用其他机制(如最终一致性)。 如需具体案例分析或 SQL 代码调试,可提供更多细节!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值