
是否使用外键约束
【强制】不得使用外键与级联,一切外键概念必须在应用层解决.-《阿里Java规范》
首先外键(Foreign Key)是什么东西
使用方案
假设有一个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);
创建一个外键索引完成这个规则
完成后的表关系如下
...
外键原理
被指向的字段,具有唯一性
可以保证成绩字段的一致性,即每一次插入一个score数据,首先要检测是否student表存在这个id,保证一致性
如果在外键类型上使用CASCADE,则会保证在做更新和删除sutudent表的student_id时,触发一次级联操作,会同步更新score表的student_id或者删除student_id.
外键类型RESTRICT 也同样会做一次检测,但不会做级联操作,而是直接拒绝操作。
...
场景思考
知道外键是什么后,我们来思考一个场景:
现在有一个电商系统,用户有一个账户id,商品有一个商品id,这两个字段和订单绑定,此时订单id和账户表ID构成一个外键关系,同时和商品表id也构成一个外键关系,那么我每次生成一笔订单,就需要向另外两张表查询检测一次数据,那么就存在几个问题:
这些问题在互联网公司会显得格外严重,因为访问流量大的时候以上问题基本上是完全无法得到MySQL系统本身解决的
同时在做分库分表设计的时候,外键约束就会显得格外离谱。
同时MySQL系统的外键设计是背离部分SQL标准的
引用自博客园Eden: (https://www.cnblogs.com/discuss/articles/1862244.html)
对SQL标准的背离:如果ON UPDATE CASCADE或ON UPDATE SET NULL递归更新相同的表,之前在级联过程中该表一被更新过,它就象RESTRICT一样动作。这意味着你不能使用自引用ON UPDATE CASCADE或者ON UPDATE SET NULL操作。这将阻止级联更新导致的无限循环。另一方面,一个自引用的ON DELETE SET NULL是有可能的,就像一个自引用ON DELETE CASCADE一样。级联操作不可以被嵌套超过15层深。
对SQL标准的背离: 类似一般的MySQL,在一个插入,删除或更新许多行的SQL语句内,InnoDB逐行检查UNIQUE和FOREIGN KEY约束。按照SQL的标准,默认的行为应被延迟检查,即约束仅在整个SQL语句被处理之后才被检查。直到InnoDB实现延迟的约束检查之前,一些事情是不可能的,比如删除一个通过外键参考到自身的记录。
处理
因为以上问题,我们通常在建模时隐性设计外键约束,实际实现采用业务逻辑模拟外键的方式处理,这样可以解决把一致性全部放在DBA上的性能问题,同时我们可以采用允许脏数据存在,然后定时数据清理的方案去保证数据处理的分时性能,避免高峰处理。
这样的好处:
解决性能问题 增加了可扩展性,框架迁移不用在数据库系统内部实现逻辑约束 分库分表的时候方便 不会在DB层面造成死锁反推:是否可以使用外键约束
我觉得在部分业务场景下是可以考虑使用的,回到最开始的例子,教务系统的成绩模块重要的点不再是性能问题,而是高可靠,因为对学校来说,系统存在以下特点:
数据量较少,一个学校学生最多不超过10万人,通常在5000-50000这个区间内,对DB来说这是一个很小的数据量 数据不容许出错,因为成绩和学生的人身利益直接挂钩 能够进行数据修改操作的用户极少,只有教务处录入成绩的老师。 如果放在业务部分,如果出现student表student_id在第一次被删除后,未清理score表数据,这个student_id短时间内被再次使用,而没有做数据清理,就容易出现成绩复用错误,诸如此类所以 不得使用外键与级联,一切外键概念必须在应用层解决。 大部分情况下正确,但同样我认为需要分业务场景解决,并不能一竿子打死。
本文内容主要参考阿里内部的Java开发手册,需要这份文档资料的,欢迎关注+转发后,私信“资料”即可查看获取方式
来吧,我们看一下这份文档内部还有哪些内容
第1章编程规约
第2章异常日志
第3章单元测试
第4章安全规约
第5章MySQL数据库
第6章工程结构
第7章设计规约
附录专有名词
...
...
篇幅原因,这里只展示其中一小部分,后期会不断更新这种好文,关注我,不迷路,谢谢~