V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JasonLaw
V2EX  ›  程序员

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

  •  
  •   JasonLaw · 2021-09-04 17:38:25 +08:00 · 16195 次点击
    这是一个创建于 1183 天前的主题,其中的信息可能已经有所发展或是发生改变。
    第 1 条附言  ·  2021-09-05 13:01:24 +08:00
    第 2 条附言  ·  2021-09-07 16:49:34 +08:00
    128 条回复    2021-09-08 13:46:02 +08:00
    1  2  
    cmdOptionKana
        1
    cmdOptionKana  
       2021-09-04 18:22:48 +08:00
    [外键应不应该建立] - V2EX 大概第 75 楼的说法归纳得比较好:

    ipwx 2020-05-20 10:13:58 +08:00

    感觉楼上有部分人是鸡同鸭讲。

    有一部分回复是支持“在恰当的场景下使用外键”,前提条件是需求明确。

    另一部分人的口吻是“弊大于利不用”。但实际上主张弊大于利的,我感受了一下,其实有个前提条件,是一开始的需求没那么明确,还要充分迭代开发,导致后面原先的外键变成了累赘。

    你们两拨人根本讨论问题的前提条件就不一样好吧。
    plko345
        2
    plko345  
       2021-09-04 18:40:19 +08:00 via Android
    有没比较优秀的项目,用或不用 外键的,理由是什么?现在看大家讨论好像很多公司默认就不允许外键,是不是表示有不少人实际没有遇到过讨论外键带来的问题的问题
    ltfree
        3
    ltfree  
       2021-09-04 18:51:31 +08:00
    不知道老外是不是也经常改需求
    Huelse
        4
    Huelse  
       2021-09-04 18:53:43 +08:00
    个人理解,这增加了理解成本、后期维护成本

    而恰好我们追求的就是低成本、低维护
    agagega
        5
    agagega  
       2021-09-04 18:58:24 +08:00 via iPhone
    Rails 默认就是不用 Foreign Key 的,很多框架默认也不用,不能直接说国外就很流行吧
    skiy
        6
    skiy  
       2021-09-04 19:02:30 +08:00
    按我曾经使用过的理解.
    (MySQL) 导出后, 再重新导入时, 如果 A 表存在的外键是 B 表的, 但导入的顺序是 A -> B, 这个时候就会报错了. 所以,我自己的话,我是不会用外键的了.
    当然, 这个用不用肯定要看应用场景的了. 存在就有其理由.
    ragnaroks
        7
    ragnaroks  
       2021-09-04 19:08:17 +08:00
    外键会降低性能,国内很少有公司会配备专业的 DBA,都是运维或者开发靠自己优化,不如就直接不要外键用业务逻辑去约束,比如插入数据之前先检查各种冲突
    Jooooooooo
        8
    Jooooooooo  
       2021-09-04 19:19:17 +08:00
    小项目用没问题
    cassyfar
        9
    cassyfar  
       2021-09-04 19:19:54 +08:00   ❤️ 2
    不选外键为啥用关系型数据库? dynamodb 不香吗?
    xstmjh
        10
    xstmjh  
       2021-09-04 19:34:12 +08:00
    不用外键不代表不去遵循范式去定义 model,只是不在数据库层面加这么一个强约束而已
    JasonLaw
        11
    JasonLaw  
    OP
       2021-09-04 19:39:04 +08:00 via iPhone
    @ltfree #3 改需求跟外键有什么关系?
    wangbenjun5
        12
    wangbenjun5  
       2021-09-04 19:39:14 +08:00
    现在国内 99%的项目都不用外键约束,包括 BAT 这些大厂的项目。通常来说,一些对数据完整性要求非常高的项目会使用,比如 OA 、金融类的,但是实际上也不一定是这样,毕竟也可以在应用层去约束。

    非要说,我觉得主要原因是为了图方便,加了外键对很多操作都有限制。。。不够灵活!带来的好处也不明显
    JasonLaw
        13
    JasonLaw  
    OP
       2021-09-04 19:40:09 +08:00 via iPhone
    @Huelse #4 外键“ 增加了理解成本、后期维护成本”?为什么?
    JasonLaw
        14
    JasonLaw  
    OP
       2021-09-04 19:50:27 +08:00 via iPhone
    @ragnaroks #7 外键会带来数据库层面上的负担,难道“ 插入数据之前先检查各种冲突”就不是负担吗?
    JasonLaw
        15
    JasonLaw  
    OP
       2021-09-04 19:57:14 +08:00 via iPhone
    @xstmjh #10

    What if someone deletes the parent while you're inserting children? Right now when I submit "add comment" -- if you have already deleted your answer, this comment is now an orphan. FKs would've prevented it.

    Precisely -- it should be the DB's job, since it's the only one that can guarantee transactionality in the face of multiple concurrent clients.

    不在数据库层面加上外键约束的话,应用代码怎么能做到数据库外键的效果?就比如第一段文字所描述的场景。
    JasonLaw
        16
    JasonLaw  
    OP
       2021-09-04 19:58:19 +08:00 via iPhone
    @wangbenjun5 #12 “方便”具体指的是什么?
    JasonLaw
        17
    JasonLaw  
    OP
       2021-09-04 20:01:28 +08:00 via iPhone
    @agagega #5 我更想知道现象背后的原因,而不是现在是怎样一个现象,欢迎分享你的想法。
    JasonLaw
        18
    JasonLaw  
    OP
       2021-09-04 20:02:43 +08:00 via iPhone
    @Jooooooooo #8 大项目用会有什么问题?顺便问一下,怎样的才是大项目?
    alamaya
        19
    alamaya  
       2021-09-04 20:22:42 +08:00
    @JasonLaw 应用层横向扩展加机器就完事儿了,数据库可没这么简单,说到底,就是让数据库做尽量简单的事,复杂的逻辑都移到应用层
    zjsxwc
        20
    zjsxwc  
       2021-09-04 20:22:50 +08:00 via Android
    不是不想用,
    而是业务复杂点,就碰到外键循环死锁依赖问题,
    真 tm 吃 shit
    JasonLaw
        21
    JasonLaw  
    OP
       2021-09-04 20:29:00 +08:00 via iPhone
    @alamaya #19 应用层实现逻辑外键的话,比如我回复主题的时候需要检查主题是否存在,应用层检查难道不会给数据库增加压力?
    JasonLaw
        22
    JasonLaw  
    OP
       2021-09-04 20:30:37 +08:00 via iPhone
    @zjsxwc #20 关于“ 外键循环死锁依赖”,可以举个简单的例子吗?
    makelove
        23
    makelove  
       2021-09-04 20:31:09 +08:00   ❤️ 1
    外键几乎没有编程上的额外负担,却能增加数据的可靠性,降低后期维护的成本
    且有时编程上也方便比如可以级联删除
    坏处无非是性能上的极少量损失,真的有热点表在乎这点损失就在那个表上不用外键就行了何必整个都不要了
    zjsxwc
        24
    zjsxwc  
       2021-09-04 20:33:35 +08:00 via Android
    @JasonLaw https://blog.csdn.net/aaaaaalll/article/details/80742064
    这种已经是简化了的场景,实际情况更加复杂,会有各种花式依赖。
    zjsxwc
        25
    zjsxwc  
       2021-09-04 20:35:40 +08:00 via Android
    不过也看业务,业务简单,后期不会修改业务,实体类关系是树状的用外键最爽。
    chinvo
        26
    chinvo  
       2021-09-04 20:45:09 +08:00
    "外键增加额外性能开销"的这类说法, 是把不用外键时执行插入操作之前的 select 的数据库性能开销+网络 IO 开销当不存在么...
    wqtacc
        27
    wqtacc  
       2021-09-04 20:50:02 +08:00
    现在的目的就是用来存数据,利用数据库的查询能力,至于外键的约束,虽然不在数据库使用,应用实现时这个关系还时有的
    JasonLaw
        28
    JasonLaw  
    OP
       2021-09-04 21:02:04 +08:00 via iPhone
    @wqtacc #27 那怎么解决#15 所描述的问题?再激进一点的话,那还要事务干嘛,直接应用层解决不行吗?干嘛让数据库做这么多事情来保证 AID ?
    alamaya
        29
    alamaya  
       2021-09-04 21:04:00 +08:00
    @JasonLaw 对读的优化手段要多少有多少,数据库读和写的吞吐量都不在一个数量级,读的性能可以靠加机器解决,写却没那么简单
    你说的这个场景,加个被动缓存就解决的事儿,对数据库来说能有多大压力呢
    wangbenjun5
        30
    wangbenjun5  
       2021-09-04 21:07:57 +08:00
    @JasonLaw 真没人用这玩意,外键这玩意就和触发器、存储过程一样,属于上古时期重数据库开发模式的遗留产物。。。不是还有什么数据库编程么,还能在数据库层写 if else,你敢用?
    adoal
        31
    adoal  
       2021-09-04 21:16:28 +08:00   ❤️ 7
    因为国内现在主流的一批所谓架构师都是 BAT 等几个互联网大厂出来的。甚至是做行业信息化的圈子也被互联网技术路线污染了,导致屁大点并发量但是业务规则较为复杂的系统也在盲目使用互联网大厂所面临的秒杀、热评场景下用的架构。
    JasonLaw
        32
    JasonLaw  
    OP
       2021-09-04 21:19:15 +08:00 via iPhone
    @alamaya #29 这样子的话,你也无法达到数据库外键所能实现的效果,应用代码还复杂多了。
    gBurnX
        33
    gBurnX  
       2021-09-04 21:21:31 +08:00   ❤️ 3
    1.外键是一种规范与约束,一定要有,但不一定要用数据库去实现。

    2.外键功能,到底用数据库去实现,还是用代码实现,要看成本。这里的成本,有员工成本,与服务器成本两个因素。

    用数据库实现外键,员工成本小,服务器成本高。但国外,员工工资贵,服务器相反会便宜些,而国内相反,这是主要因素。

    3.国外人少,内卷小,而且各自行业规范多,员工职业素质高,做事规范,自然要用外键,这是原因二。反观国内,竞争太激烈了,甲方爸爸加需求完全不管合同,乙方不给加,甲方爸爸分分钟找个新乙方做,而且事情传出去,以后乙方不用混了。

    甲方爸爸天天加需求,数据库表一天加好几个,你要用外键,每次还得梳理一遍,很麻烦。

    4.外键不是造成死锁的原因,避免死锁,首先要设计表要规范化,其次在事务中需要锁表时,一定要按同意顺序进行锁表。
    gBurnX
        34
    gBurnX  
       2021-09-04 21:24:57 +08:00
    @skiy

    你说的这个缺点,其实正是外键的优点。

    你想想,如果没有外键来约束导入顺序,你先导入 A,怎么保证 A 的数据中包含的 B 就一定正确?正确包括且不限于 B 存在且不冲突。
    gBurnX
        35
    gBurnX  
       2021-09-04 21:25:34 +08:00
    @ragnaroks

    如果你用的词是 [降低性能] ,那么不用外键,也会降低性能的。仔细想想?
    gBurnX
        36
    gBurnX  
       2021-09-04 21:26:23 +08:00
    @alamaya 为什么应用层能横向扩展加机器,数据库就不行?
    gBurnX
        37
    gBurnX  
       2021-09-04 21:30:21 +08:00   ❤️ 1
    @zjsxwc

    造成死锁有很多原因,比如建表建模不规范,或者锁表不按统一顺序,或者业务冲突等等。

    你提的这个原因,是属于建表建模不规范。

    本来已经有了岗位表,来说明每个员工在部门里的位置,为什么非得在部门表里再设计一个对于岗位的冗余?而且还是拿员工表来做的冗余,这种设计,直接导致两个表互为外键,属于错误的建模建表方式。正确的做法,应该是在岗位表里添加字段,来说明员工在部门里的位置。并且包含关系,从大到小依次为:部门 -> 岗位 -> 员工。
    JasonLaw
        38
    JasonLaw  
    OP
       2021-09-04 21:30:22 +08:00 via iPhone
    @zjsxwc #24 个人感觉,这个是设计的问题,不是外键的问题,不在部门表存储负责人就行了,使用关系表解决。想象一下,把三个实体当作 Spring 中的 service 的话,它们也会造成循环依赖。
    JasonLaw
        39
    JasonLaw  
    OP
       2021-09-04 21:34:05 +08:00 via iPhone
    @gBurnX #37 同意
    zjsxwc
        40
    zjsxwc  
       2021-09-04 21:43:01 +08:00 via Android
    @JasonLaw #15

    实际业务中 不怕数据多了,而是怕数据丢失,
    comment 变 orphan 不可怕,甚至业务中你的 answer 也不是真删除而只是把 status 字段改成某个代表删除的值,于是对于这种业务来说外键约束级联删除都触发不了,这种伪删除的业务存在导致了数据库外键作用消失,外键只剩下 index 加速查询的作用。


    忽然想到,

    要用外键的一个 非常非常 重要的前提是你 真正的真实的 在删数据!!!!!!!而不是标记下伪删除。
    JasonLaw
        41
    JasonLaw  
    OP
       2021-09-04 22:02:09 +08:00 via iPhone
    @zjsxwc #40 是有一点道理,不过要保证 referential integrity,也只有数据库外键才能做到。
    alamaya
        42
    alamaya  
       2021-09-04 22:08:35 +08:00 via iPhone
    @gBurnX 我可没说不行,只是说没应用层这么简单。也确实没接触过业界有哪些广泛使用、成本低、伸缩自如的数据库集群方案
    yhkang
        43
    yhkang  
       2021-09-04 22:09:44 +08:00
    我都快忘了物理外键有什么作用了
    qinwangzeng
        44
    qinwangzeng  
       2021-09-04 22:12:54 +08:00
    本小白懂了,以后也不用外键,多一事不如少一事
    ospider
        45
    ospider  
       2021-09-04 22:15:49 +08:00
    先问是不是,再问为什么……从你发的几个链接我得不出国外都推荐使用外键这个结论来
    ericls
        46
    ericls  
       2021-09-04 22:26:31 +08:00 via iPhone
    不用外键的话 不是得用事务去处理约束? 不是更麻烦吗?
    pigspy
        47
    pigspy  
       2021-09-04 22:27:25 +08:00
    我其实挺喜欢外键,个人觉得这玩意是关系型数据库的核心功能之一
    但是奈何面向业务编程时,你很难保证领域模型是稳定且准确的,一旦不是,后期的维护和修改的成本会很大,所以干脆在应用层来自己完成外键的逻辑
    kkbblzq
        48
    kkbblzq  
       2021-09-04 22:30:13 +08:00
    程序规模不大的话,也没有性能要求的话,随便吧,否则从程序设计的角度上看,外键会使你的程序对底层 DB 强依赖,其实是不利于程序的扩展的,在不少的分层设计中,仓储层是需要无感知能替换切换的,用了外键就不能实现这一点,而且用了外键,分库分表的之类的就没办法做了,一些分布式的 DB 也基本不支持外键,当 DB 成为瓶颈点再来大改的话反而更麻烦。
    另外那个评论的例子,我想知道有没有什么其他有实际意义的场景,以这个例子来看,参照完整性好像没有什么意义,就算是子表多了一条脏数据,其实对业务本身是没有关系的,完全可以后续再清理。
    kekxv
        49
    kekxv  
       2021-09-04 22:30:45 +08:00 via iPhone
    讲真的,用数据库进行约束,比应用层去搞好多了,因为你永远不知道下一个改代码的人会对他做什么🤔
    而且,你的或者你们公司项目真的在乎那点性能吗?
    BraveChi
        50
    BraveChi  
       2021-09-04 22:34:15 +08:00
    @adoal 认识深刻,现在动不动就高并发高可用,其实就是个内网系统,架构整的太大,运行效果反而不如老系统设计合理
    mxT52CRuqR6o5
        51
    mxT52CRuqR6o5  
       2021-09-05 00:14:41 +08:00 via Android
    是不是经常改需求就不适合用外键?
    akira
        52
    akira  
       2021-09-05 00:25:29 +08:00
    外键这种东西,谁爱用谁用,反正我不用
    JerryCha
        53
    JerryCha  
       2021-09-05 00:25:36 +08:00   ❤️ 1
    因为我们的系统要出生就支持 114514 用户并发,1 分钟承担 1919810 的 transaction
    Rocketer
        54
    Rocketer  
       2021-09-05 00:29:43 +08:00 via iPhone
    一楼已经说得很清楚了,就是因为需求不明确。

    这都不是需求方本身的问题,而是行业发展太快,想明确都明确不了。功能做一半,项目就被砍,然后又去做别的,这都是常态。

    现在分布式数据库又不追求强一致,都是最终一致性,你以为数据应该存在而实际找不到的情况是必须要兼容的,这就更不需要外键了。
    agagega
        55
    agagega  
       2021-09-05 00:35:56 +08:00
    @JasonLaw
    前面有层楼也说了,现在的业务有些稀奇古怪的做法,比如假删除,对应到数据库,外键的意义就少了很多。还有一点原因,互联网公司的业务不太追求完整的数据一致性,而外键有可能会影响性能。打个不太合适的比喻:你生活在一个很久都没有确诊病例的地方,打疫苗的意愿肯定不会那么高,因为疫苗副作用概率>感染的概率。

    不过,这些习惯可能也就为了降低对开发者数据库技能的要求,把数据库下沉,单纯当一个存内容的地方。所谓保证可移植性这个就比较扯淡了。即使不用外键这些东西,一个 MySQL 的项目要转 PG 也没那么容易的。如果是 scope 很清楚的项目,复杂 join 、存储过程、视图这些用得飞起,应该能轻松不少的。
    coolcfan
        56
    coolcfan  
       2021-09-05 02:10:38 +08:00 via Android
    好奇 WordPress 用没用外键
    passerbytiny
        57
    passerbytiny  
       2021-09-05 05:44:43 +08:00 via Android
    你首先的区分应用编程还是数据库编程。国内不管哪种语言、用不用 ORM 、用全 ORM 还是用半 ORM,都是应用编程,主编程语言是高级编码语言而不是 SQL,这种情况下物理外键是负担(但逻辑外键仍然存在)。而国外,既有应用编程,也有数据库编程,你用外键作为关键字搜索,结果自然倾向于数据库编程。
    iPhone9
        58
    iPhone9  
       2021-09-05 05:51:19 +08:00
    因为大部分人都玩不转啊
    elboble
        59
    elboble  
       2021-09-05 06:20:30 +08:00 via Android
    Python 用 orm 如果不用外键不如直接用 nosql 吧! 萌新看得满头大汗
    JasonLaw
        60
    JasonLaw  
    OP
       2021-09-05 06:54:59 +08:00 via iPhone
    @Rocketer #54

    我还是不明白,为什么“需求不明确”就开始开工了?就不建议使用外键了?

    你说的分布式数据库指的是 NoSQL 数据库吗?
    JasonLaw
        61
    JasonLaw  
    OP
       2021-09-05 07:00:49 +08:00 via iPhone
    @agagega #55 同意你所说的“ 互联网公司的业务不太追求完整的数据一致性”
    JasonLaw
        62
    JasonLaw  
    OP
       2021-09-05 07:02:55 +08:00 via iPhone
    @passerbytiny #57 什么叫数据库编程?什么叫应用编程?
    Rocketer
        63
    Rocketer  
       2021-09-05 07:42:38 +08:00 via iPhone
    @JasonLaw 因为踩的坑多了,就知道外键对改需求来说是个累赘。你要非让我具体说个案例,你一定觉得这也没啥,但架不住多啊!烦啊!

    关键是为啥一定要用外键?奥卡姆剃刀原则在设计系统时一样有效——如无必要,勿增实体。不能因为它在某些角度看起来有好处就无脑用。

    一致性对某些场景很重要,但多数互联网企业并不在意这个。据说连支付宝的交易记录都允许出错,每天都会因为对不上账而赔几万块钱。但你要他保证一致性,那成本怕是远远远远远超每天几万块。
    JasonLaw
        64
    JasonLaw  
    OP
       2021-09-05 08:18:36 +08:00 via iPhone
    @agagega #55
    @JasonLaw #61
    @Rocketer #63

    这里纠正一下,外键跟一致性( Consistency )没有太大的关系,更正确的说法是:外键能够保证 referential integrity 。Consistency 不是数据库本身的一个特性,它是应用怎么使用数据库的一个特性。
    zjsxwc
        65
    zjsxwc  
       2021-09-05 08:25:48 +08:00 via Android
    illuz
        66
    illuz  
       2021-09-05 09:07:32 +08:00 via Android
    为什么拿 stack overflow 这样的学术性偏强的问答帖子和 v 站水帖比,你可以去搜搜 reddit 帖子或去老外的技术群组讨论,确认一下老外确实大部分都会用 FK 再来提问。
    或者直接去捞一些 GitHub 项目,统计一下 FK 的使用情况。
    反正我随便搜了一下,发现老外们也有不少不用 FK 的,类似你这样的帖子讨论也不少。
    JasonLaw
        67
    JasonLaw  
    OP
       2021-09-05 10:23:00 +08:00
    @illuz #66 能够提供一下你所查阅的资料吗?我看到的绝大多数都是倾向使用外键的。

    Google 搜索“foreign key database or application reddit”得到的一些结果:

    * Are foreign keys really necessary in a database design? : programming - https://www.reddit.com/r/programming/comments/6xidz/are_foreign_keys_really_necessary_in_a_database/
    * What Would Happen If Foreign Keys Not Used? : Database - https://www.reddit.com/r/Database/comments/ef38aq/what_would_happen_if_foreign_keys_not_used/
    * Don't Most Databases Have Foreign Key Constraints? : SQLServer - https://www.reddit.com/r/SQLServer/comments/6wz2hu/dont_most_databases_have_foreign_key_constraints/dmd7dj1/
    * ...
    fengpan567
        68
    fengpan567  
       2021-09-05 10:28:28 +08:00
    其实我也不喜欢用,为什么要让数据库去约束业务
    zjsxwc
        69
    zjsxwc  
       2021-09-05 10:37:25 +08:00 via Android
    @gBurnX #37
    并没觉得有问题,而且业务上负责人不是岗位。
    我觉得没必要为了使用外键而修改业务。
    JasonLaw
        70
    JasonLaw  
    OP
       2021-09-05 10:39:38 +08:00 via iPhone
    @fengpan567 #68 请问你有使用 primary key, unique 等数据库提供的约束吗?有使用事务吗?
    msg7086
        71
    msg7086  
       2021-09-05 11:03:58 +08:00
    所以你说的是数据库物理外键还是说框架里的逻辑外键?

    我觉得逻辑外键要有,但是物理外键还是算了吧。
    物理外键删数据的时候连 hook 都不会走,天知道会漏跑什么东西。
    JasonLaw
        72
    JasonLaw  
    OP
       2021-09-05 11:10:46 +08:00
    @msg7086 #71

    更加准确的说,是数据库的 foreign key constraint 。

    “物理外键删数据的时候连 hook 都不会走,天知道会漏跑什么东西”是什么意思?可以具体描述一下吗?
    msg7086
        73
    msg7086  
       2021-09-05 11:24:19 +08:00
    @JasonLaw ORM 在执行数据写入的时候是可以往上挂钩子做额外处理的。

    举个简单的例子。你有两个表,一个用户表,一个帖子表,帖子外键连到用户。

    现在你把一个用户删了,那么这个用户对应的帖子就需要做适当的处理。
    比如你可以在用户的删除钩子上,把他发的所有帖子的用户 ID 都改成一个特殊用户的 ID 。
    这样那些帖子就不会被删除了。
    当然,这个钩子里还可以做其他的操作,比如更新用户的发帖数,更新页面缓存,等等。

    而用数据库外键的话,你一做级联删除,这些帖子就消失不见了,你也没法在对象上加钩子来处理这些事件。
    换句话说,数据库背着程序做了一些修改,而程序没有感知。
    这个行为我觉得是很可怕的。

    所以我宁愿让 ORM 来负责一致性。我想级联删除就级联删除,我想用钩子做其他处理,就绑钩子。
    JasonLaw
        74
    JasonLaw  
    OP
       2021-09-05 11:30:04 +08:00
    @msg7086 #73 ON DELETE CASCADE 是可选的。
    msg7086
        75
    msg7086  
       2021-09-05 12:20:07 +08:00
    @JasonLaw 但是做外键不就是为了强一致性么。
    JasonLaw
        76
    JasonLaw  
    OP
       2021-09-05 12:28:16 +08:00 via iPhone
    @msg7086 #75 保证 referential integrity
    illuz
        77
    illuz  
       2021-09-05 12:28:39 +08:00 via Android
    @JasonLaw 你这几个帖子里面就有一些非外键派的吧,没有绝大部分哈
    illuz
        78
    illuz  
       2021-09-05 12:35:42 +08:00 via Android
    我只是对帖上文的 stack overflow 和 v2 对比方式表达一下看法,感觉这个讨论前提不够有力
    skiy
        79
    skiy  
       2021-09-05 12:51:42 +08:00
    @gBurnX 关于准确这个方面, 这个说得没错. 但你 `mysqldump` 出来一个库后, 然后直接 `source` 进去却报错. 这个你受得了? 试想几百个表, 然后还要一个一个地对比着先后顺序吗? 如果没有文档说明, 没人会想到会是这种问题. 当初我就是吃过这方面的亏. (我不是专业的 DBA)
    azuis
        80
    azuis  
       2021-09-05 12:53:50 +08:00 via iPhone
    @skiy 导入导出的时候问题不大吧。可以暂时禁用外键检查,导入完成后再启用即可。
    JasonLaw
        81
    JasonLaw  
    OP
       2021-09-05 12:54:56 +08:00 via iPhone
    @illuz #78 所以我在#67 贴出了 reddit 上的一些讨论
    makelove
        82
    makelove  
       2021-09-05 13:08:06 +08:00
    @skiy 先不说我从来没碰到过导入导出会出现外键问题(印象中会自动禁用外键),即使有你不可以先禁止外键检查导入完后再启用吗
    noparking188
        83
    noparking188  
       2021-09-05 13:19:29 +08:00
    好奇是不是基于单机数据库来讨论的
    skiy
        84
    skiy  
       2021-09-05 13:36:00 +08:00
    @makelove 我都说当初了. 默认是不禁止的. 当时还不了解, 以为程序有问题... 折腾.
    JasonLaw
        85
    JasonLaw  
    OP
       2021-09-05 13:57:06 +08:00 via iPhone
    @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
        86
    Rache1  
       2021-09-05 14:00:27 +08:00
    @skiy 这个倒是可以在 SQL 前后临时禁用外键,一般 SQL 插入的时候也可以,就是要多敲一个命令,比较麻烦
    skiy
        87
    skiy  
       2021-09-05 14:06:13 +08:00
    @JasonLaw 存在就有其道理. 使用场景的问题. 我刚才就说我不是专业的 DBA. 对于这块不了解, 也没打算要对这块有多了解.
    ikas
        88
    ikas  
       2021-09-05 14:06:49 +08:00
    往前推 10 多年吧,那时候 db 设计比现在严格多了..何止外键.表的关系都是严格设计.class 也都是严格写好映射....
    只不过是软件行业发展很快,需求大,变化多,时间少....
    skiy
        89
    skiy  
       2021-09-05 14:09:25 +08:00
    @Rache1 知道. 当初用的人家的一个开源的网站程序. 备份时直接用的 mysqldump 备份了. 有时候基础命令我很少带参数去操作的, 都是用默认的设置. 后来解决了.
    wqtacc
        90
    wqtacc  
       2021-09-05 14:13:59 +08:00
    @JasonLaw #15 的问题,不就是插入前检查父属性存不存在的问题么,把检查和插入写一个事务里,这样也是符合程序逻辑的;你看没有事务的情况下,在程序里是一个完整的过程。我们不用外键,也不是连事务一起都丢掉。
    wangxin13g
        91
    wangxin13g  
       2021-09-05 14:25:21 +08:00
    个人看法是 外键 触发器 存储过程都是面向实现的依赖 具体点说就是你依赖了某个 SQL 数据库的某个功能,这对后面无论是迁移数据库还是接手的人理清逻辑都是百害无一利的
    fkdog
        92
    fkdog  
       2021-09-05 14:58:16 +08:00
    @JasonLaw 外键的两个用途,级联更新和级联删除。
    级联更新:大部分的外键一般都是设置为主键或者其他值固定的字段,因此级联更新一般意义不大。
    级联删除:为了监管 /数据误操作恢复等原因,大型互联网公司基本都是采用 update 软删除而非 delete 物理删除,因此级联删除意义也不大。

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

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

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

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

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

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

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

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

    理想主义者遍地,孤芳自赏,还要抱团取暖。
    JasonLaw
        100
    JasonLaw  
    OP
       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/
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   917 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 20:54 · PVG 04:54 · LAX 12:54 · JFK 15:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.