主键

概念

学生表(学号,姓名,性别,班级) 学号是一个主键

课程表(课程号,课程名,学分) 课程号是一个主键

成绩表(学号,课程号,成绩) 学号和课程号的属性组构成一个主键

成绩表中的学号不是成绩表的主键,不过是学生表的主键,成绩表的外键,同理课程号也是成绩表的外键

定义

如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键

以一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表

主键的设计原则

1)主键应当是对用户没有意义的

2)主键应该是单列的,以提高连接和筛选操作的效率

复合键的使用通常出于两点考虑:

a)主键应当具有意义—–这为认为的破坏数据库提供了方便

b)在描述多对多关系的连接表中可以使用两个外部键作为主键——该表可能成为其他从表的主表,并成为从表的主键的一部分,使得之后的从表包含更多的列

3)永远不要更新主键

4)主键不应该包含动态变化的数据(时间戳等)

5)主键应当由计算机自动生成

数据库主键选取策略

建立数据库的时候,需要为每张表指定一个主键(一个表只能有一个主键,但是可以有多个候选索引)

常见的主键选取方式有:

1)自动增长型字段

自动增长型主键会省略很多繁琐的工作,但在数据缓冲模式下,不能预先填写主键与外键的值

Order(OrderID,OrderDate)   //主键OrderID是自动增长型字段

OrderDetail(OrderID,LineNum,ProductID,Price)

如果要在Order表中插入一条记录,在OrderDetail表中插入若干条记录,为了能在OrderDetail表中插入正确的OrderID字段,必须先更新Order表以获得系统系统分配的OrderID,但是为了确保数据一致性,Order表和OrderDetail表必须在事务保护下同时进行更新,这显然是矛盾的

除此之外,当需要在多个数据库之间进行数据复制时,自动增长型字段可能造成主键冲突

2)手动增长型字段

3)使用UniqueIdentifier SQL Server提供一个UniqueIdentifier数据类型(16字节),并提供一个生成函数NEWID(),生成一个唯一的UniqueIdentifier

4)使用COMB类型

保留UniqueIdentifier的前10字节,后6字节表示生成时间

分布式唯一标识

个人比较偏好生成一个分布式唯一标识:

分布式 id

外键

外键(FK)是用于建立或加强两个表数据之间的链接的一列或多列。

通过将表中主键值的一列或多列添加到另一个表中,可创建两个表之间的连接,这个列就成为第二个表的外键

外键的作用

外键用于保持数据一致性,完整性

主要目的是控制存储在外键表中的数据

例子

  • 删除约束

FK约束的目的是控制存储在外表中的数据,同时可以控制对主键表中数据的修改

例如:publishers表中记录出版商的信息,titles表中记录书的信息,如果在publishers的表中删除一个出版商,而这个出版商的ID在titles表中记录书的信息时被使用了,则这两个表之间关联的完整性将被破坏,即titles表中该出版商的书籍因为与publisher表中的数据没有链接而变的孤立。

FK约束可以防止这种情况的发生,如果主键表中数据的更改使得与外键表中数据的链接失效,则这种更改是不能实现的;如果试图删除主键表中的行或试图修改主键值,而该主键值与另一个表的FK约束值相关,则该操作不可实现。

若要成功的更改或删除FK约束的行,可以现在外键表中删除外键数据或更改外键数据,然后将外键连接到不同的主键数据上去

  • 变更

外键主要是用来控制数据库中的数据完整性的,当对一个表的数据进行操作时,和他有关联的一个表或多个表的数据能够同时发生改变 

例子:

A(a,b) :a为主键,b为外键(来自于B.b)
B(b,c,d) :b为主键
A中的b字段要么为空,要么为B表中存在的b值

实战中不推荐使用外键索引

首先我们明确一点,外键约束是一种约束,这个约束的存在,会保证表间数据的关系“始终完整”。

因此,外键约束的存在,并非全然没有优点。比如使用外键,可以:

  1. 保证数据的完整性和一致性;

  2. 级联操作方便;

  3. 将数据完整性判断托付给了数据库完成,减少了程序的代码量;

然而,鱼和熊掌不可兼得。外键是能够保证数据的完整性,但是会给系统带来很多缺陷。正是因为这些缺陷,才导致我们不推荐使用外键,具体如下:

性能问题

假设一张表名为user_tb。那么这张表里有两个外键字段,指向两张表。

那么,每次往user_tb表里插入数据,就必须往两个外键对应的表里查询是否有对应数据。

如果交由程序控制,这种查询过程就可以控制在我们手里,可以省略一些不必要的查询过程。

但是如果由数据库控制,则是必须要去这两张表里判断。

并发问题

在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。

若是在高并发大流量事务场景,使用外键更容易造成死锁。

扩展性问题

这里主要是分为两点:

  1. 做平台迁移方便,比如你从Mysql迁移到Oracle,像触发器、外键这种东西,都可以利用框架本身的特性来实现,而不用依赖于数据库本身的特性,做迁移更加方便。

  2. 分库分表方便,在水平拆分和分库的情况下,外键是无法生效的。将数据间关系的维护,放入应用程序中,为将来的分库分表省去很多的麻烦。

技术问题

使用外键,其实将应用程序应该执行的判断逻辑转移到了数据库上。

那么这意味着一点,数据库的性能开销变大了,那么这就对DBA的要求就更高了。

很多中小型公司由于资金问题,并没有聘用专业的DBA,因此他们会选择不用外键,降低数据库的消耗。

相反的,如果该约束逻辑在应用程序中,发现应用服务器性能不够,可以加机器,做水平扩展。

如果是在数据库服务器上,数据库服务器会成为性能瓶颈,做水平扩展比较困难。

参考资料

主键和外键

数据库中为什么不推荐使用外键约束?