简介
本文介绍数据库的外键的用法以及其缺点。
实际上,项目中很少用到外键,因为缺点很明显,《阿里巴巴 Java 开发手册》也明确写明不要使用物理外键。
创建外键的条件
- 父表和子表引擎一致,否则报错;
- 保证表的存储引擎为InnoDB,否则虽然不报错但是无约束(只有index);
- 实际上,如果两个表都是MyISAM 引擎的,错误根本不会发生,但也不会产生外键。
- 两个字段数据类型一致,数据显示长度可以不同,另外需注意有符号无符号,必须一致;
- 两个字段都添加了独立索引,如果子表外键字段无索引,那么在创建外键的时候会自动先添加索引;如果父表字段无索引,将报错;
- 字段是否允许为空,在更新或删除时候对外键字段操作有关;如外键创建时候 on delete set null on udpate cascade。但是子表外键字段不允许为空, 矛盾,创建不成功。
- 字段的字符集和校对集(外键类型为字符的时候);
- 外键名字不能重复,是针对数据库而不是表;
- 子表外键字段 – 数据 为父表引用字段子集;
- 字段可能为混合键值中一个,没有自己独立索引。外键字段必须有自己独立索引。
外键的作用
外键默认的作用有两点:一个对父表,一个对子表(外键字段所在的表)。
作用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;
不要用外键索引
阿里手册:
物理外键的优点
- 保证数据的完整性和一致性
- 级联操作方便
- 将数据完整性判断托付给了数据库完成,减少了程序的代码量
- 如果通过物理外键解决我的bug比逻辑外键更简单
物理外键的缺点
- 性能问题
- 每次向A表中插入数据都会去B表查询是否有对应数据。如果不止一个外键或者批量插入/更新呢,性能会很差
- 并发问题
- 在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁。
- 扩展性问题
- 比如表结构重构,mysql迁移到oracle,分表分库,是不是会顿时感到头痛
请先
!