是否使用外键约束

【强制】不得使用外键与级联,一切外键概念必须在应用层解决.-《阿里Java规范》

首先外键(Foreign Key)是什么东西

使用方案

为什么大多数互联网公司不用外键约束-LMLPHP

假设有一个score表 id是自增id,score是分数,student_id是学号。

另一个student表,id是自增id,name是名字,student_id是学号。

那么设计这个的时候就希望有一个关联关系,让score的student_id指向student表的student_id,存在一个学生对应多个成绩的关系。所以我可以使用以下SQL语句

ALTER TABLE score
ADD CONSTRAINT FOREIGN KEY (student_id)
REFERENCES student(student_id);

创建一个外键索引完成这个规则

完成后的表关系如下

为什么大多数互联网公司不用外键约束-LMLPHP

外键原理

被指向的字段,具有唯一性

可以保证成绩字段的一致性,即每一次插入一个score数据,首先要检测是否student表存在这个id,保证一致性

如果在外键类型上使用CASCADE,则会保证在做更新和删除sutudent表的student_id时,触发一次级联操作,会同步更新score表的student_id或者删除student_id.

外键类型RESTRICT 也同样会做一次检测,但不会做级联操作,而是直接拒绝操作。
为什么大多数互联网公司不用外键约束-LMLPHP

场景思考

知道外键是什么后,我们来思考一个场景:

现在有一个电商系统,用户有一个账户id,商品有一个商品id,这两个字段和订单绑定,此时订单id和账户表ID构成一个外键关系,同时和商品表id也构成一个外键关系,那么我每次生成一笔订单,就需要向另外两张表查询检测一次数据,那么就存在几个问题:

  • 增删改触发这个查询操作的性能消耗,服务器系统是否允许
  • 在其他表查询会上需要对其他表做一个内部锁,是否存在高并发死锁情况
  • 数据一致性全部交给数据库服务器,数据库服务器是否能够承受

这些问题在互联网公司会显得格外严重,因为访问流量大的时候以上问题基本上是完全无法得到MySQL系统本身解决的

同时在做分库分表设计的时候,外键约束就会显得格外离谱。

同时MySQL系统的外键设计是背离部分SQL标准的

引用自博客园Eden: (https://www.cnblogs.com/discuss/articles/1862244.html)

处理

因为以上问题,我们通常在建模时隐性设计外键约束,实际实现采用业务逻辑模拟外键的方式处理,这样可以解决把一致性全部放在DBA上的性能问题,同时我们可以采用允许脏数据存在,然后定时数据清理的方案去保证数据处理的分时性能,避免高峰处理。

这样的好处:

  • 解决性能问题
  • 增加了可扩展性,框架迁移不用在数据库系统内部实现逻辑约束
  • 分库分表的时候方便
  • 不会在DB层面造成死锁

反推:是否可以使用外键约束

我觉得在部分业务场景下是可以考虑使用的,回到最开始的例子,教务系统的成绩模块重要的点不再是性能问题,而是高可靠,因为对学校来说,系统存在以下特点:

  • 数据量较少,一个学校学生最多不超过10万人,通常在5000-50000这个区间内,对DB来说这是一个很小的数据量
  • 数据不容许出错,因为成绩和学生的人身利益直接挂钩
  • 能够进行数据修改操作的用户极少,只有教务处录入成绩的老师。
  • 如果放在业务部分,如果出现student表student_id在第一次被删除后,未清理score表数据,这个student_id短时间内被再次使用,而没有做数据清理,就容易出现成绩复用错误,诸如此类

所以 不得使用外键与级联,一切外键概念必须在应用层解决。 大部分情况下正确,但同样我认为需要分业务场景解决,并不能一竿子打死。

08-27 18:22