又是一个关于外键的问题

2022-01-23 03:55:18 +08:00
 lanlanye

我知道这个问题在站内已经讨论过无数次了,比如 关于外键,为什么国内基本都不推荐使用,国外基本都推荐使用?,但是直到现在也没有一个帖子能够达成共识,所以就一些不明确的地方提出些问题:

首先说前提:

  1. 首先关系库不可能放弃关联,所以这里讨论的不是"是否需要外键"而是"是否需要物理外键"。
  2. 既然用到外键,使用前提必然是对数据引用完整性(参照完整性)有一定要求的,我看到有人说业务对报错敏感的时候不使用,这点我不能理解,毕竟插入一条错误的数据和插入数据时失败同样是错误,当用户添加数据成功但找不到这条数据时,问题应该比添加失败更为严重才对。
  3. 外键对性能的影响在数据量不大时应该是不需要考虑的,这里讨论的共识应该是避开数据量极大的表(如日志表)。
  4. 针对分布式,我认为这个和上一条是一样的,当存储需要用到分布式时,说明数据量已经相当大,这种情况下自然不需要考虑外键。
  5. 由于软删除存在,级联删除意义不大,我们可以约定建立外键是不设置级联删除(或阻止删除被引用的数据以保证数据完整)。

接下来是疑问:

  1. 在避开单表大量数据和分布式存储的情况下,对于数据量通常不超过百万且经常需要插入和更新(软删除也算更新)的业务数据,物理外键是否优于逻辑外键?
  2. 对于开头链接中 15 楼的问题,一般默认的隔离级别( RR )并不能避免这个问题发生,业务约束同样需要对数据库加锁,且更依赖业务人员的水平,这是否可以说明逻辑外键对比物理外键并无优势?还是说有更好的方式能够解决这种问题?
  3. 有人提出导入表的顺序问题,我认为导入前整理数据之间的关系是很合理的要求,何况检查也可以被关闭,这一点并不能作为物理外键不好的理由,此外把外键设置成环同样是一个设计错误,并不是外键本身的问题。
  4. 即使考虑数据的积累,过早的禁止外键是否真的合理?当数据膨胀到使用外键会产生明显问题时再去除外键是否更合理?毕竟过早的设计会导致开发人员付出大量额外的工时来保证数据完整。

出于以上几点疑问,我感觉逻辑外键相比物理外键来说毫无优势(包括性能优势,因为需要加锁),还很可能因开发人员水平不足、考虑不周或在直接修改数据库时写错脚本从而损坏数据,那么为什么仍有相当多的开发者认为多数情况下应该避免使用物理外键呢?

以下是一些个人的想法:

大厂全面禁止外键,一方面可能是由于核心业务对性能敏感而不使用,最后为了管理方便干脆全部禁止,我至今没能找到一个合理的全面禁止使用外键的理由,如果有大厂高层,希望可以听到你们的看法。

在设计阶段加入外键,一定程度上可以降低开发人员的编码负担,减少系统错误,哪怕是不会考虑并发状况或对数据库不熟悉的开发者,外键也能阻止他将错误的数据写入库中,反过来产生的「接口总是报错」、「导入顺序不对报错」等问题,我认为是合理且必要的错误提示,一个接口要做的应该是在数据不合规范时阻止其写入,而不是强行写进去。

另外还有一个相关但关系不是很大的问题:

说到外键就一定会说到关联,我注意到也有部分人反对在业务查询中使用 JOIN ,主要理由是 JOIN 的效率低下,关于这个问题,希望有熟悉数据库的人能为我解惑:

如果表设计合理,关联查询是否都可以通过索引优化到比多次查询并在内存中拼接的方式更快?

以上。请注意我讨论这些问题的前提都是设计合理,对于数据库本身设计就无法很好的支撑业务,导致经常需要走弯路解决问题的情况,不属于本帖的讨论范围。

3527 次点击
所在节点    数据库
47 条回复
xsm1890
2022-01-24 16:34:14 +08:00
国内基本都不推荐,国外基本都推荐。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
dcoder
2022-01-24 17:28:07 +08:00
辟谣针对外键的谣言: "国外基本都推荐外键"

我就在硅谷, 一般都是在写 micro/distributed services, 基本不会写外键
从我认识的人里聊天, 基本没见着用外键的,
除非是针对 legacy services, 单体 DB 那种才会用...
cwek
2022-01-24 21:16:58 +08:00
@lanlanye 物理外键还要考虑修改到时需要校验主键。多一层 IO 读。物理外键等应该是数据库主管业务时产生的强逻辑验证,既然业务迁移到应用上,物理外键就可以被“业务”的概念外键取代。
liub34177
2022-01-25 11:39:01 +08:00
cus
2022-01-25 13:08:12 +08:00
如果外键已经导致了性能问题
再去考虑性能优化?
aguesuka
2022-01-25 16:57:36 +08:00
外键只是一个运行时保险丝, 编码时还是要捕捉外键抛出的异常. 无论是不是使用外键, 问题应该要在测试时暴露出来. 也就是说外键不会减少工作量, 也不会减少 bug, 只是能降低 bug 的影响.

另外, 本来想打一堆字的, 不过 tricks 太恰当了.
liprais
2022-03-01 11:18:47 +08:00
@fengjianxinghun sql 就是图灵完备的通用语言
你们不喜欢 sql 就说不喜欢呗,如果真的要喷高低也 fact check 一下...

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/830011

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX