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

大家在正式的环境里面会使用外键么

  •  
  •   yeyuexia · 2015-11-19 14:11:23 +08:00 · 12988 次点击
    这是一个创建于 3052 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近和几个同事聊及对 mysql 的使用的时候,纷纷对我们项目并不使用外键而在逻辑上维护 table 之间的关系这种事情感到不可思议,说“怎么能这么做呢,为什么不用外键?” 这件事情简直对我产生了巨大的冲击,难道还有人会在生产环境上使用外键么? 如果是的话,能否告诉我为什么要这么做,好处有哪些? 谢谢
    第 1 条附言  ·  2015-11-19 15:05:32 +08:00
    其实我主要是想知道什么时候需要使用外键。为什么。
    在我经验来看只有银行,金融这种对数据非常敏感的地方可能需要外键。 实际情况下还有那些方面呢?

    参考:
    http://stackoverflow.com/questions/83147/whats-wrong-with-foreign-keys
    57 条回复    2019-01-24 18:50:09 +08:00
    wshcdr
        1
    wshcdr  
       2015-11-19 14:35:27 +08:00
    我不用,我是用代码来维护外键关系
    mikan
        2
    mikan  
       2015-11-19 14:38:08 +08:00
    表结构由 hibernate 维护,自带外键
    clinkzlol
        3
    clinkzlol  
       2015-11-19 15:26:08 +08:00
    如果使用 ORM 框架来处理数据层,基本上就没外键什么事了
    dong3580
        4
    dong3580  
       2015-11-19 15:27:18 +08:00
    以前经常用,现在不用了。校验在程序里。
    kafka0102
        5
    kafka0102  
       2015-11-19 15:33:42 +08:00
    不要用框架的实现来规避问题。外键是对数据一致性的约束,使用代码逻辑约束是通常的做法,个人也赞成这种做法。数据库能做很多事情,外键、触发器、存储过程等等,如果你愿意,可以把逻辑都写到数据库端,但这对数据库的维护会带来很多麻烦。如果你是使用 mongodb 等,那你也只能写到代码里。但另一方面,代码逻辑写得再好,也可能造成数据不一致的。所以,如果数据一致性很重要,那么数据库做约束可能是更好的选择。
    yeyuexia
        6
    yeyuexia  
    OP
       2015-11-19 15:39:45 +08:00
    @clinkzlol
    @mikan
    使用框架和使用外键是两回事吧?
    pythoner
        7
    pythoner  
       2015-11-19 15:47:57 +08:00
    我的经验是,在项目初期,为了快速完成功能开发,还是会按照规范添加外键。
    随着业务的发展,很多时候,我们需要按照业务功能来做垂直拆分,这个时候就必须得去掉一些外键,以及相关的 join 查询
    asj
        8
    asj  
       2015-11-19 16:10:01 +08:00   ❤️ 2
    我做过几年的银行业软件,就我见到的系统确实很多生产环境中是不用外键的。原因和正式代码中是否使用断言类似。理论上来说,这些约束有助于维护系统的一致性。但是问题在于,不一致了,又怎么样呢?

    在理想世界里,不论数据,代码还是输入输出都是有条有理的。而现实中却常常要面对历史遗留代码和数据,低质量的输入,以及变化无常的输出等等不那么有条理的情况。那么,比如一个系统运行到某个点时,发现某项检查与预期不一致的时候。停止整个系统真的是最好的选择么?又或者输入数据的时候某个字段不一致,因而拒绝整条数据真的是最合理的么?

    另一个问题是不方便,当需要批量表数据,调整表结构或者更改程序逻辑的时候,这些约束经常会对开发人员造成困扰。虽然一般来说各种数据库都有暂时关闭约束的机制,但是确实把问题复杂化了。
    而且,不同于 UI 上对随机的普通用户操作的校验,或者系统对恶意攻击者的安全性检查,数据库外键这类约束阻止掉的操作,来自于系统的开发和维护人员。一般来说他们最了解系统的内部结构,很清楚自己在做什么,对系统的一切有着最高的权限。这时候他们更容易觉得这种约束烦人而不是可靠。
    felixzhu
        9
    felixzhu  
       2015-11-19 16:12:51 +08:00
    外键相对来说还是可以用的,这种强一致性的约束通过代码是有可能出问题的
    主要问题感觉是后面分库分表的时候比较麻烦吧
    SmiteChow
        10
    SmiteChow  
       2015-11-19 17:11:27 +08:00
    如果没有强理由,外健是物理和逻辑上最好的选择,上面说到的数据历史问题,说明技术选型不是很对,参看 Django Migration 。
    zyAndroid
        11
    zyAndroid  
       2015-11-19 17:52:09 +08:00   ❤️ 1
    没有外键的话代码得写成什么样子,无法想象。
    jhaohai
        12
    jhaohai  
       2015-11-19 18:18:40 +08:00 via iPhone
    数据库里少用这些东西,太耗性能
    jsjscool
        13
    jsjscool  
       2015-11-19 20:03:53 +08:00
    WEB 开发的瓶颈多半在 IO ,需要权衡。没有哪个一定好或者一定不好。
    g67261831
        14
    g67261831  
       2015-11-19 20:24:07 +08:00
    @asj 赞同。
    JamesRuan
        15
    JamesRuan  
       2015-11-19 20:35:52 +08:00
    用外键不需要理由,不用外键才需要找理由。
    slayerdoomsday
        16
    slayerdoomsday  
       2015-11-19 20:38:06 +08:00
    我们 CTO 说,一不要用外键,二不要用 join ,把需要的数据都查出来再用程序处理
    JamesRuan
        17
    JamesRuan  
       2015-11-19 20:42:32 +08:00
    @slayerdoomsday 那还要用数据库干嘛,直接自己写入文件好了。
    xuwenhao
        18
    xuwenhao  
       2015-11-19 20:44:57 +08:00 via iPhone
    @JamesRuan 如果你要让数据库可以水平拆库扩展只能选择这么做
    Infernalzero
        19
    Infernalzero  
       2015-11-19 20:50:16 +08:00
    @JamesRuan
    上面已经有人说了, web 主要瓶颈在数据库,互联网模式一般都是只让数据库做最简单的储存,不做复杂查询,复杂的事交给 web 服务器做, web 服务器的数量是可以不断加的,而 master db 对于某个用户来说是唯一的,不可分割的,而且现在服务器的性能一般的计算根本不算什么
    bravecarrot
        20
    bravecarrot  
       2015-11-19 20:51:22 +08:00 via iPad
    涨姿势了。一直以为都用外键的!
    JamesRuan
        21
    JamesRuan  
       2015-11-19 20:57:19 +08:00
    @xuwenhao 既然要 sharding 不能重建吗?

    我一直认为 sharding 是 poor man's temporary resort ,通过增加复杂性,换取局部的优势通常都不是最佳选择。
    JamesRuan
        22
    JamesRuan  
       2015-11-19 20:59:29 +08:00
    @Infernalzero 我知道很多都这样做。就是牺牲潜在的一致性风险,换取速度和灵活性。

    那为什么不多弄弄分布式储存?无非还是成本问题咯。
    JamesRuan
        23
    JamesRuan  
       2015-11-19 21:02:12 +08:00
    @Infernalzero 只做简单储存的,是不是用持久化的 Redis 之类才是最佳选择?或者上 NoSQL 。

    用 RMDBS 不用外键都是需要找理由的。
    Lpl
        24
    Lpl  
       2015-11-19 21:09:00 +08:00 via Android
    从来没用过显式外建,关系在代码维护。为了好移植,因为没有专业 dba ,存储过程还有触发器都没有用
    rail4you
        25
    rail4you  
       2015-11-19 21:34:46 +08:00
    楼主的项目没那么复杂,不用外键可以理解。
    楼主用的技术是 sql 中的反模式,剑走偏锋,在个别环境下有效而已
    但反过来,楼主居然说用外键不可思议,这只能说明楼主不习惯用关系数据库,或者没见过复杂的关系数据库。

    对于很多大型关系数据库项目,用外键是常态,因为外键是关联表的标准技术,合理的使用外键,能减少数据冗余,避免数据表结构的逻辑错误。比如企业的 erp 项目,表和表的关联极多,不用外键维持表关联(比如删除更新关联数据),会增加极多客户端的工作量。
    HentaiMew
        26
    HentaiMew  
       2015-11-19 21:41:34 +08:00
    有外键“关系”不是绝对需要外键“约束”,可以存在有主外键关系的多张表字段但不存在约束的情况。
    Ouyangan
        27
    Ouyangan  
       2015-11-19 21:43:51 +08:00
    长姿势了,还有不用外键的啊 ,我要好好深究一下
    Infernalzero
        28
    Infernalzero  
       2015-11-19 21:45:35 +08:00
    @JamesRuan 看业务了,核心业务一般不会上 nosql 的,普通的技术团队很难保证其稳定性,redis 也多数只是用于 session 和缓存
    dawncold
        29
    dawncold  
       2015-11-19 21:49:34 +08:00
    数据库表结构也一定程度上反映业务,所以能加的外键全都会加,另外也见识过完全不用外键的某系统,国内该行业前三的公司,可能是因为性能考虑,但我感觉除非是写的不好,否则一般不会遇到性能问题,就算遇到了也不应该从这些地方入手改进
    teek
        30
    teek  
       2015-11-19 21:53:53 +08:00
    新浪现在貌似也不用外键,据说损失过数据。
    JamesRuan
        31
    JamesRuan  
       2015-11-19 22:01:58 +08:00
    @teek 这样说就有些偏颇了,就像说:“乔布斯貌似也不看西医,据说西医没有治好过肿瘤”。

    外键不会保证数据不损失,但是外键可以保证数据不会因为人为的“缺陷”而损失。这种“缺陷”,往往在逻辑稍微复杂一点的环境下非常容易发生。
    initialdp
        32
    initialdp  
       2015-11-19 22:05:00 +08:00
    从来不使用外键,原因:
    ( 1 )穷,没有专业的 DBA ;
    ( 2 )约束关系在存储过程或者代码逻辑里实现;
    ( 3 )迁移、检查、数据输入等操作都比较方便。
    teek
        33
    teek  
       2015-11-19 22:31:50 +08:00
    @JamesRuan 嗯嗯,我描述上有点问题,是造成了损失,对象不是数据。具体就不能细说吧。
    xuyinan503
        34
    xuyinan503  
       2015-11-19 22:32:39 +08:00
    添加外键,如果要改表结构,相应要改的东西太多。

    而且从业务上来讲,一般数据都不会做级联的物理删除。

    比如说,删除一个客户,不会删掉这个客户的订单。连这个客户都不会删掉,而只是变一下标志位。

    否则回头查不到自己有什么业务,跟谁发生了什么业务。
    msg7086
        35
    msg7086  
       2015-11-19 22:35:19 +08:00
    @JamesRuan 很多时候用 RDBMS 纯粹是因为运维好招罢了。
    另外很多时候生产环境的持久存储端并不是固定的一个数据库。比如可能会从 MySQL 转向 Postgres 或者 Oracle ,这种时候平台无关的好处就体现出来了。像触发器,外键这种东西,都可以在框架里用事务来实现,不再需要依赖 DBMS 本身的特性了。甚至如你所说的改用 Redis 或者 MongoDB 都是有可能的。(当然 Redis 是不太可能了)
    sobigfish
        36
    sobigfish  
       2015-11-19 23:15:42 +08:00
    看业务逻辑,比如你把用户删除了,他的帖子怎么办?如果是软删除的话 外键的 on delete 肯定不好用。。
    xuwenhao
        37
    xuwenhao  
       2015-11-19 23:50:42 +08:00
    @JamesRuan 如果 Model 层直接用好 ActiveRecord 的 OR Mapping 模型,本来就不存在 join ,不存在新增复杂性的问题
    xuwenhao
        38
    xuwenhao  
       2015-11-19 23:52:24 +08:00
    实际大部分互联网应用实践中,都是通过应用来维护关联,而不是数据库外键,这个有很多原因
    1. 性能损耗
    2. 迁移数据时候的一致性维护

    大部分互联网应用对于一致性要求都很低,包括大部分企业应用也是这样
    loveyu
        39
    loveyu  
       2015-11-20 00:09:04 +08:00
    为啥没人提到开发环境上外键,测试环境直接移除外键呢
    yuriko
        40
    yuriko  
       2015-11-20 08:35:30 +08:00
    即使 mysql 我怎么记得外键是只有少数引擎才支持的东西啊……
    abscon
        41
    abscon  
       2015-11-20 09:27:46 +08:00
    @clinkzlol ORM 的实现也会用外键
    exit0
        42
    exit0  
       2015-11-20 09:52:04 +08:00
    这类系统 etl 操作多,表约束成了障碍。
    lilydjwg
        43
    lilydjwg  
       2015-11-27 18:28:38 +08:00
    我一直很疑惑的是,为什么很多人不用外键不用约束,却要手工连上数据库操作?把数据改坏了怎么办?我在学校时就见过一个坏得完全没法用的数据库。

    当然 MySQL 的实现若是有问题那当我没说。
    yeyuexia
        44
    yeyuexia  
    OP
       2015-12-02 11:28:43 +08:00
    @lilydjwg
    @SmiteChow
    @rail4you
    统一回复下吧 个人觉得使用外键开保证数据一致的话,不可避免的会造成扩展性的损失。因为你依赖了大量的外部的工具来保证了你程序的逻辑完备,一旦需要数据迁移到其他数据库上时,会造成很大的麻烦。而且使用外键,实际上是将一些逻辑服务器的压力转嫁到了 mysql 上,如果你的系统只是服务于小量级的用户的话,那用用也没什么,一旦用户量变大,后果不堪设想。还有就是手动改数据库而不是用接口去改这个事情,个人表示佩服。
    rail4you
        45
    rail4you  
       2015-12-02 12:32:42 +08:00
    @yeyuexia mysql 的功能和性能也很强,不是 toy 级别的数据库。楼主总是说 mysql 只能服务小数量级用户(什么算小?),用户量变大不可想象,这些都是主观臆断。现实中 mysql 使用外键的例子太多 ,这些 mysql 应用的用户量都不小,数据库端的逻辑也没那么简单。

    数据库的迁移一直很麻烦,不单单是 mysql ,数据验证还有用户逻辑也有很多选择,有丰富经验 dba 的情况下,在数据库端做这些也无妨。

    数据库环境很复杂的,每种数据库都有自己擅长的领域。楼主的数据库经验只限于一定领域,建议把话题限制下,说明下数据库应用范围(比如 xxx 类的互联网应用),这样能减少很多争议。
    SmiteChow
        46
    SmiteChow  
       2015-12-02 15:47:29 +08:00
    数据库表的定义必然在程序里啊,现在都是 ORM ,如果楼主认为 mysql 的外健性能还不如你程序自己写的话可能楼主是撸 C 的大牛。
    zonghua
        47
    zonghua  
       2016-08-11 01:00:49 +08:00
    外键不利于集群
    ezreal
        48
    ezreal  
       2016-10-11 14:30:21 +08:00
    公司线上数据库全面禁止外键!
    ezreal
        49
    ezreal  
       2016-10-11 14:32:12 +08:00
    外键高并发下会有性能问题,以及容易造成死锁?
    rosalindest
        50
    rosalindest  
       2016-12-07 15:43:32 +08:00
    大半年前你说你在 thoughtworks ,小女子现在有一事相求!!!!!!!!
    虽然你很久不上 V2 ,看到可否回复!!!!!!!!!!!!!!!
    yeyuexia
        51
    yeyuexia  
    OP
       2016-12-08 14:04:52 +08:00
    @rosalindest 啥事?
    rosalindest
        52
    rosalindest  
       2016-12-19 14:07:49 +08:00
    啊啊啊啊居然被看到了!!抱歉现在才回复!!!我想问你还在 TW 么??
    yeyuexia
        53
    yeyuexia  
    OP
       2016-12-21 14:49:52 +08:00
    @rosalindest 在 有事私聊吧 [email protected] 我邮箱
    Pegasus
        54
    Pegasus  
       2017-02-16 00:22:13 +08:00
    最近阿里放出来的《 java 开发规范》里明确说了不用外键。
    liuxin5959
        55
    liuxin5959  
       2017-02-17 18:05:53 +08:00
    @Pegasus 看来可以终结这个问题了,马云爸爸都说不要用了。😂
    Mark24
        56
    Mark24  
       2017-03-01 14:35:39 +08:00
    @Infernalzero 感谢

    长知识了

    一直以为一定要外键。
    kanezeng
        57
    kanezeng  
       2019-01-24 18:50:09 +08:00
    其实对大多数量小的项目来说,我觉得用不用外键看自己吧。对于量很大的项目,很可能实施了一大堆微服务,严格来说每个微服务应该有自己背后的的数据库,这时候比如用户表和帖子表可能都在不同的数据库服务器里的,也就没有人考虑外键的问题了。不过大多这种情况下,直接 nosql 更好一点。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3342 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 00:05 · PVG 08:05 · LAX 17:05 · JFK 20:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.