关于外键,为什么国内基本都不推荐使用,国外基本都推荐使用?

2021-09-04 17:38:25 +08:00
 JasonLaw

国内的一些讨论:

国外的一些讨论:

16450 次点击
所在节点    程序员
128 条回复
JasonLaw
2021-09-05 12:54:56 +08:00
@illuz #78 所以我在#67 贴出了 reddit 上的一些讨论
makelove
2021-09-05 13:08:06 +08:00
@skiy 先不说我从来没碰到过导入导出会出现外键问题(印象中会自动禁用外键),即使有你不可以先禁止外键检查导入完后再启用吗
noparking188
2021-09-05 13:19:29 +08:00
好奇是不是基于单机数据库来讨论的
skiy
2021-09-05 13:36:00 +08:00
@makelove 我都说当初了. 默认是不禁止的. 当时还不了解, 以为程序有问题... 折腾.
JasonLaw
2021-09-05 13:57:06 +08:00
@skiy #84 让我想起了以下这几段话。(来源: https://stackoverflow.com/a/85298/5232255

This is an issue of upbringing. If somewhere in your educational or professional career you spent time feeding and caring for databases (or worked closely with talented folks who did), then the fundamental tenets of entities and relationships are well-ingrained in your thought process. Among those rudiments is how/when/why to specify keys in your database (primary, foreign and perhaps alternate). It's second nature.

If, however, you've not had such a thorough or positive experience in your past with RDBMS-related endeavors, then you've likely not been exposed to such information. Or perhaps your past includes immersion in an environment that was vociferously anti-database (e.g., "those DBAs are idiots - we few, we chosen few java/c# code slingers will save the day"), in which case you might be vehemently opposed to the arcane babblings of some dweeb telling you that FKs (and the constraints they can imply) really are important if you'd just listen.

Most everyone was taught when they were kids that brushing your teeth was important. Can you get by without it? Sure, but somewhere down the line you'll have less teeth available than you could have if you had brushed after every meal. If moms and dads were responsible enough to cover database design as well as oral hygiene, we wouldn't be having this conversation. :-)
Rache1
2021-09-05 14:00:27 +08:00
@skiy 这个倒是可以在 SQL 前后临时禁用外键,一般 SQL 插入的时候也可以,就是要多敲一个命令,比较麻烦
skiy
2021-09-05 14:06:13 +08:00
@JasonLaw 存在就有其道理. 使用场景的问题. 我刚才就说我不是专业的 DBA. 对于这块不了解, 也没打算要对这块有多了解.
ikas
2021-09-05 14:06:49 +08:00
往前推 10 多年吧,那时候 db 设计比现在严格多了..何止外键.表的关系都是严格设计.class 也都是严格写好映射....
只不过是软件行业发展很快,需求大,变化多,时间少....
skiy
2021-09-05 14:09:25 +08:00
@Rache1 知道. 当初用的人家的一个开源的网站程序. 备份时直接用的 mysqldump 备份了. 有时候基础命令我很少带参数去操作的, 都是用默认的设置. 后来解决了.
wqtacc
2021-09-05 14:13:59 +08:00
@JasonLaw #15 的问题,不就是插入前检查父属性存不存在的问题么,把检查和插入写一个事务里,这样也是符合程序逻辑的;你看没有事务的情况下,在程序里是一个完整的过程。我们不用外键,也不是连事务一起都丢掉。
wangxin13g
2021-09-05 14:25:21 +08:00
个人看法是 外键 触发器 存储过程都是面向实现的依赖 具体点说就是你依赖了某个 SQL 数据库的某个功能,这对后面无论是迁移数据库还是接手的人理清逻辑都是百害无一利的
fkdog
2021-09-05 14:58:16 +08:00
@JasonLaw 外键的两个用途,级联更新和级联删除。
级联更新:大部分的外键一般都是设置为主键或者其他值固定的字段,因此级联更新一般意义不大。
级联删除:为了监管 /数据误操作恢复等原因,大型互联网公司基本都是采用 update 软删除而非 delete 物理删除,因此级联删除意义也不大。

此外,由于现代大型系统都是分布式的。如果你的外键对象不在一个库里那么你无法加上外键约束。此外由于一些数据库中间件,读写分离、高可用、异步等,可能会出现数据延迟的情况,那么这种情况可能就会出现外键异常导致插入失败的情况。

所以综上,外键约束在大型系统里是一个很鸡肋的存在。
至于你说的 orphan 问题,外键检查完全是可以通过代码来实现的。

外键这东西放到今天其实有点学院派的味道,而且很多国外会用到外键也是由于类似 hibernate orm 之类的东西在后边自动帮你加上去了。这东西和数据库范式是一个道理,理论上说遵循范式不要有数据冗余,但是实际上冗余数据在今天反而是一个解决跨库跨表非常常用的手段。
fengpan567
2021-09-05 15:04:33 +08:00
@JasonLaw 请问你是喜欢偷换概念吗,primary key, unique 是针对另外一张表做约束的?
makelove
2021-09-05 15:57:02 +08:00
@fkdog 不要拿极端用例当普遍情况,你所说的大型互联网公司里的大型应用又有几个呢?一般单个数据库一个主从足够了,特别是现在有了大容量高速 SSD 不是千万级以上的用户还真用不上极端的性能手段。
fkdog
2021-09-05 16:12:51 +08:00
@makelove
删除一个 user,直接清空了与该 user 有关联的所有表的所有数据,这就是你期望看到的?
如果碰到根据实际场景动态决定是否需要删除关联表的数据的需求,你设置这种外键不是给自己找麻烦?

就算设置外键,一般也是关联到主键 id 或者其他一些不变的唯一 id 上,这种场合下你级联更新用得上地方?

连这些都被你描述成“极端场景”了,那我也建议你不要拿你的那点 simple crud 经验指点别人。再不多长进长进,过几年 crud 自动生成插件都能把你给淘汰了呢。
sy20030260
2021-09-05 16:28:00 +08:00
这个问题本身并不成立。
参考 https://github.com/github/gh-ost/issues/331#issuecomment-266027731,这个 issue 里提到 Github 从来不使用外键。
外键的使用并没有国内外之分,而更多和系统体量相关。大型微服务系统里几乎不会用到外键。
sy20030260
2021-09-05 16:28:23 +08:00
JasonLaw
2021-09-05 16:31:35 +08:00
@fkdog #95 希望大家都就事论事,不要说有的没的。外键最重要的作用是保证 referential integrity 。关于“ 删除一个 user,直接清空了与该 user 有关联的所有表的所有数据”,这个是显式声明了 ON DELETE CASCADE 。
fkdog
2021-09-05 16:48:01 +08:00
@JasonLaw 建议你不要把这些东西当成圣经。

我举个很常见的例子,比如很多数据库为了索引优化,会给索引列设置一个默认值避免因为 NULL 造成的无效索引,比如给字段设置-1,0 等。假如这一列恰好是外键列,而-1,0 对应在父表中的关联对象是不存在的,这显然是违反了引用完整性。

所以你是要考虑你理想的引用完整呢,还是考虑实用主义优化性能为主?

我发现 v2 上的人真的很喜欢把一些东西当圣经奉为圭臬。。
比如 restful 和业务 code 封装就能争出上百条评论。

理想主义者遍地,孤芳自赏,还要抱团取暖。
JasonLaw
2021-09-05 16:48:19 +08:00
@sy20030260 #96 我只是说国外大多数人会选择使用外键。

你应该是从这个 Reddit 找到那个 issue 的。

https://www.reddit.com/r/programming/comments/dtls30/at_github_we_do_not_use_foreign_keys_ever_anywhere/

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

https://tanronggui.xyz/t/799876

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

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

© 2021 V2EX