我知道这个问题在站内已经讨论过无数次了,比如 关于外键,为什么国内基本都不推荐使用,国外基本都推荐使用?,但是直到现在也没有一个帖子能够达成共识,所以就一些不明确的地方提出些问题:
首先说前提:
- 首先关系库不可能放弃关联,所以这里讨论的不是"是否需要外键"而是"是否需要物理外键"。
- 既然用到外键,使用前提必然是对数据引用完整性(参照完整性)有一定要求的,我看到有人说业务对报错敏感的时候不使用,这点我不能理解,毕竟插入一条错误的数据和插入数据时失败同样是错误,当用户添加数据成功但找不到这条数据时,问题应该比添加失败更为严重才对。
- 外键对性能的影响在数据量不大时应该是不需要考虑的,这里讨论的共识应该是避开数据量极大的表(如日志表)。
- 针对分布式,我认为这个和上一条是一样的,当存储需要用到分布式时,说明数据量已经相当大,这种情况下自然不需要考虑外键。
- 由于软删除存在,级联删除意义不大,我们可以约定建立外键是不设置级联删除(或阻止删除被引用的数据以保证数据完整)。
接下来是疑问:
- 在避开单表大量数据和分布式存储的情况下,对于数据量通常不超过百万且经常需要插入和更新(软删除也算更新)的业务数据,物理外键是否优于逻辑外键?
- 对于开头链接中 15 楼的问题,一般默认的隔离级别( RR )并不能避免这个问题发生,业务约束同样需要对数据库加锁,且更依赖业务人员的水平,这是否可以说明逻辑外键对比物理外键并无优势?还是说有更好的方式能够解决这种问题?
- 有人提出导入表的顺序问题,我认为导入前整理数据之间的关系是很合理的要求,何况检查也可以被关闭,这一点并不能作为物理外键不好的理由,此外把外键设置成环同样是一个设计错误,并不是外键本身的问题。
- 即使考虑数据的积累,过早的禁止外键是否真的合理?当数据膨胀到使用外键会产生明显问题时再去除外键是否更合理?毕竟过早的设计会导致开发人员付出大量额外的工时来保证数据完整。
出于以上几点疑问,我感觉逻辑外键相比物理外键来说毫无优势(包括性能优势,因为需要加锁),还很可能因开发人员水平不足、考虑不周或在直接修改数据库时写错脚本从而损坏数据,那么为什么仍有相当多的开发者认为多数情况下应该避免使用物理外键呢?
以下是一些个人的想法:
大厂全面禁止外键,一方面可能是由于核心业务对性能敏感而不使用,最后为了管理方便干脆全部禁止,我至今没能找到一个合理的全面禁止使用外键的理由,如果有大厂高层,希望可以听到你们的看法。
在设计阶段加入外键,一定程度上可以降低开发人员的编码负担,减少系统错误,哪怕是不会考虑并发状况或对数据库不熟悉的开发者,外键也能阻止他将错误的数据写入库中,反过来产生的「接口总是报错」、「导入顺序不对报错」等问题,我认为是合理且必要的错误提示,一个接口要做的应该是在数据不合规范时阻止其写入,而不是强行写进去。
另外还有一个相关但关系不是很大的问题:
说到外键就一定会说到关联,我注意到也有部分人反对在业务查询中使用 JOIN ,主要理由是 JOIN 的效率低下,关于这个问题,希望有熟悉数据库的人能为我解惑:
如果表设计合理,关联查询是否都可以通过索引优化到比多次查询并在内存中拼接的方式更快?
以上。请注意我讨论这些问题的前提都是设计合理,对于数据库本身设计就无法很好的支撑业务,导致经常需要走弯路解决问题的情况,不属于本帖的讨论范围。