所有分类
  • 所有分类
  • 未分类

数据库–外键–用法/缺点

简介

本文介绍数据库的外键的用法以及其缺点。

实际上,项目中很少用到外键,因为缺点很明显,《阿里巴巴 Java 开发手册》也明确写明不要使用物理外键。

创建外键的条件

  1. 父表和子表引擎一致,否则报错;
  2. 保证表的存储引擎为InnoDB,否则虽然不报错但是无约束(只有index);
    1. 实际上,如果两个表都是MyISAM 引擎的,错误根本不会发生,但也不会产生外键。
  3. 两个字段数据类型一致,数据显示长度可以不同,另外需注意有符号无符号,必须一致;
  4. 两个字段都添加了独立索引,如果子表外键字段无索引,那么在创建外键的时候会自动先添加索引;如果父表字段无索引,将报错;
  5. 字段是否允许为空,在更新或删除时候对外键字段操作有关;如外键创建时候 on delete set null on udpate cascade。但是子表外键字段不允许为空, 矛盾,创建不成功。
  6. 字段的字符集和校对集(外键类型为字符的时候);
  7. 外键名字不能重复,是针对数据库而不是表;
  8. 子表外键字段 – 数据 为父表引用字段子集;
  9. 字段可能为混合键值中一个,没有自己独立索引。外键字段必须有自己独立索引。

外键的作用

外键默认的作用有两点:一个对父表,一个对子表(外键字段所在的表)。

作用1:对子表约束

子表数据在进行写操作(增和改)的时候,如果对应的外键字段在父表找不到对应的匹配,那么操作会失败。插入的时候外键字段值要么为null,要么为父表中字段。

INSERT INTO my_foreign1 VALUES(null,'嘉嘉',12,40)

插入的40为外键字段数据,在父表中对应的被引用字段无此数据值,则会报如下错误: 

作用2:对父表约束

父表数据进行写操作(删和改:都必须涉及到字段本身),如果对应的字段在子表中已经被数据所引用,那么就不允许操作。

INSERT INTO my_foreign1 VALUES(null,'皇甫',18,2)
INSERT INTO my_foreign1 VALUES(null,'嘉嘉',12,4)

在父表上进行UPDATE/DELETE以更新或删除在子表中有一条或多条对应匹配行的候选键时,父表的行为取决于:在定义子表的外键时指定的ON UPDATE/ON DELETE子句。

外键约束模式

外键约束有三种约束模式:都是针对父表; 对应MySQL有四种约束:RESTRICT,NO ACTION , CASCADE , SET NULL。

关键字含义示例
RESTRICT如果子表中有匹配的记录,则不允许对父表对应候选键进行UPDATE/DELETE操作。 (这是默认设置,也是最安全的设置)ALTER TABLE my_foreign1 add CONSTRAINT fk_id
FOREIGN KEY (p_id) REFERENCES  p_user_2(id) 
ON DELETE NO ACTION ON UPDATE NO ACTION; — 或者
ON DELETE RESTRICT ON UPDATE RESTRICT;
NO ACTION与RESTRICT相同。
CASCADE在父表上UPDATE/DELETE时,同步UPDATE/DELETE掉子表的匹配记录ALTER TABLE my_foreign1 add CONSTRAINT fk_id
FOREIGN KEY (p_id) REFERENCES  p_user_2(id) 
ON DELETE CASCADE ON UPDATE CASCADE;
SET NULL在父表上UPDATE/DELETE时,将子表上匹配记录的列设为NULL。 外键置为NULL的前提是对应字段允许为空,否则外键创建不成功。ALTER TABLE my_foreign1 add CONSTRAINT fk_id
FOREIGN KEY (p_id) REFERENCES  p_user_2(id) 
ON DELETE SET NULL ON UPDATE SET NULL;
SET DEFAULT父表有变更时,子表将外键列设置成一个默认的值。但Innodb不能识别。

通常一个合理的做法(约束模式)是:删除的时候子表置空,更新的时候子表级联:ON DELETE SET NULL ON UPDATE CASCADE;

不要用外键索引

阿里手册:

物理外键的优点

  1. 保证数据的完整性和一致性
  2. 级联操作方便
  3. 将数据完整性判断托付给了数据库完成,减少了程序的代码量
  4. 如果通过物理外键解决我的bug比逻辑外键更简单

物理外键的缺点

  1. 性能问题
    1. 每次向A表中插入数据都会去B表查询是否有对应数据。如果不止一个外键或者批量插入/更新呢,性能会很差
  2. 并发问题
    1. 在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁。
  3. 扩展性问题
    1. 比如表结构重构,mysql迁移到oracle,分表分库,是不是会顿时感到头痛
1

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录