初涉后台开发未深,诚惶诚恐看 <SQL 反模式>,学习最佳实践。
发现现在这个项目应用了不少反模式,其中最让我困惑的是,完全不使用外键,涉及到外键的逻辑全部在应用代码里实现。
询问项目负责人,告知不使用数据库外键主要有两原因:
听起来只要是分布式的服务器热升级(第一点原因)、或者团队里有运维人员,就应该抛弃数据库外键了。
业界都是这样做的吗?书上说的当真脱离了实际?
谢不吝赐教。
1
SpicyCat 2016-11-22 14:44:44 +08:00
没有设计过数据库,比较好奇完全不用外键的数据库怎么搞。就拿课本上的例子来说吧,一个选课系统,有学生信息,课程信息,还有选课信息。学生信息和课程信息可以没有外键,是独立的,那选课信息怎么搞?
|
2
6IbA2bj5ip3tK49j 2016-11-22 14:47:15 +08:00
@SpicyCat 存学生和课程的 id 啊。
|
3
windowsma 2016-11-22 14:48:21 +08:00 3
约束一般在逻辑里实现。
|
8
tabris17 2016-11-22 14:58:00 +08:00
对数据一致性要求不严格的场景都不应该使用外键
|
9
xrlin 2016-11-22 14:59:00 +08:00
外键会用,但约束在程序逻辑中实现。
|
10
helloccav 2016-11-22 14:59:13 +08:00
除了大学学数据库的时候了解过外键,工作几年来写代码从来没用过外键
|
12
windowsma 2016-11-22 15:01:29 +08:00
@ljk 企业内应用一般会用外键、触发器、存储过程等,互联网服务很少用,维护起来复杂。目前数据库能用到的约束也就是唯一索引了。
|
14
ljk OP @windowsma 请问不使用外键的话,数据库里出现的一些垃圾数据,比如一些"外键"指向不存在的记录,怎么处理?定时执行脚本清理吗?
|
18
hyuwang 2016-11-22 15:10:31 +08:00 3
性能啊
这都分布式了 当然不考虑外键 外键本身是为了实现强一致性 跟大规模并发和分布式都是冲突的 我觉得完全取决于架构如何设计 一些对性能没追求或者数据极其重要,可靠性要求高的业务外键是不可少的 |
19
windowsma 2016-11-22 15:18:00 +08:00
@ljk 额,你这是有数据库数据洁癖啊~~也没有绝对的舍弃外键,像金融、供应链等场景还是需要优先保证数据完整性的。不过在互联网业务场景下,会尽量减少连接查询,降低表之间的逻辑耦合,当所谓的外键记录不存在时,指定默认值或者直接报错。
|
20
ljk OP @hyuwang 多谢赐教。
再问一下,如果不使用数据库外键,程序中想在插入一条记录时验证某个"外键"是否合法有效,一般的做法是怎样? 是先做一次 sql 查询外键有有效性,再做一次 sql 插入?抑或是一句 sql 把校验和插入给完成了? |
21
aias 2016-11-22 15:21:57 +08:00
一般都是在逻辑里实现的..
|
22
ljk OP |
23
windowsma 2016-11-22 15:32:11 +08:00
@ljk 报错就会有日志,有日志就会有警报,有警报就得去处理。如果频繁出现垃圾数据,肯定是逻辑有问题,不得去优化逻辑么。。。从来不会无缘无故产生垃圾数据。
|
25
QK8wAUi0yXBY1pT7 2016-11-22 15:35:31 +08:00
SET FOREIGN_KEY_CHECKS=0;
|
26
liprais 2016-11-22 15:37:32 +08:00
因为 mysql 外键性能垃圾,另外你们所谓的避免 join 也是因为 mysql join 性能垃圾.......
|
27
Zzzzzzzzz 2016-11-22 15:40:32 +08:00
主要是互联网领域井喷期当时 mysql 3.xx 和 4.0x 时期并不支持外键、子查询、事务、视图。。。。
|
28
jessynt 2016-11-22 15:43:03 +08:00
Please see this question: http://stackoverflow.com/questions/83147/whats-wrong-with-foreign-keys
|
29
incompatible 2016-11-22 15:44:57 +08:00 3
保证约束需要消耗额外的计算资源。应用服务器相对数据库服务器来说更容易水平扩展,因此约束就放在业务逻辑里做了。
|
30
augustG 2016-11-22 15:47:21 +08:00
当数据达到一个量级需要分表分库时 外键绝对让你欲仙欲死
|
31
Ouyangan 2016-11-22 15:50:29 +08:00
@SpicyCat 只是不采用数据库的外键 , 一般表关联采用逻辑关联 , 双向就建一张中间表 .列定义两张表的 id. 其实是一样的,只是人为实现了数据库本身提供的功能 , 好处看楼上回帖就好了. 没有谁对谁错 ,工作经验多了自然知道自己该选择哪种方式了,都是看场景.
|
32
Mogugugugu 2016-11-22 16:14:02 +08:00
通过事务控制吧
|
33
6IbA2bj5ip3tK49j 2016-11-22 16:14:35 +08:00
@ljk 我学生用 mysql 存的,课程放在 redis 里面。怎么用外键?
|
35
eslizn 2016-11-22 16:20:30 +08:00
性能开销和维护成本,还不利于扩展
|
36
sunsh217 2016-11-22 16:29:09 +08:00
大概只有学生才会讨论这种问题。
|
37
sunsh217 2016-11-22 16:31:02 +08:00
也只有学生才会严格按教科书的做法去设计表吧。 其实教科书上的规范,根本不是业界的做法。 也可能中国的教科书,都是一帮不思进取的老帮瓜写的。
|
38
guoqiao 2016-11-22 16:41:29 +08:00
刚毕业的时候,我也觉得既然数据库提供了外键,就应该尽量使用它,这样可以让数据库自动保持数据的完整性。
不过后来发现,一旦使用了外键,那么当数据库变得庞大,就没法进行拆分。 比如说在 Django 里,可以支持多个数据库,但是却不支持夸数据库的外键。 这也意味着,如果你计划在将来将数据存放到多个数据库里,就应该一开始避免外键。 |
39
ljk OP @sunsh217 [<SQL 反模式>]( https://book.douban.com/subject/6800774/ )不是教科书,也不是中国人写的。
|
41
pangliang 2016-11-22 16:53:27 +08:00
约束在表结构里,
那逻辑变动 约束就会变动, 而 约束变动, 表结构就要变动 而表结构变动, 就要死人 |
42
Midnight 2016-11-22 16:57:47 +08:00
所谓的抛弃外键不是说连关系都不要了,只是没有在表上建外键这个属性,场景不同方案不同啦,分布式的话有外键同步会很麻烦,单数据库应用就无所谓啦
|
43
mko0okmko0 2016-11-22 17:14:14 +08:00 2
没抛弃过,但如果不用外键甚至不用拆表我就满足了性能,体积,设计复杂度.我何必用外键?
应该说,我何必用传统标准资料库? 例如 TokuDB,这是 Mysql 体系可选用的一个核心.本身是列式仓储.具有资料本身就是索引,结构本身就是有压缩的树状仓储.用了它,很多拆表的意义就没了.没有拆表何来外键? 例如 XML/json 文档式资料库.保持了包含关系也不需要序列号,每份文档的位置都已经代表他自己.或是每个 KEY 跟 VALUE 在资料库底层都已经有他自己的唯一值了. 而且文档资料库还解决了相同目标栏位不同内容物的问题, 标准资料库如果相同栏位要放不同子表的内容,设计上你会想死. 如果唯一键不是数字,又要给该表一个序列号,这样有比较省吗?各方面大都没有. 将标准资料库 /nosql 资料库一起比较关联查询,为什么这样说?因为就是有需求阿,尤其初期就没考虑到的东西突然发现要查他们关联后的条件查询结果.尤其是其中有些表没有序列号了,关联的值就是用文字. 那时候都有性能一样低下的问题.....一样低下的结果,那又何必分有拆表没拆表?有序列号没序列号? 用过更好的解决办法,你还会回头用标准资料库或是放弃更好的非标准资料库吗? 以上工作实务经验,依照每个人经验不同应该会有不同的解答,以上纯分享. |
44
tumbzzc 2016-11-22 17:18:19 +08:00
以用 flask 写网站为例,逻辑都是自己写的,没必要用外键
|
45
hyuwang 2016-11-22 17:21:06 +08:00
@ljk 不是专业后端,从运维角度来说的,我数据库开发经验不多,我个人是不会在写入数据时候做效验的,我只会效验数据本身,然后写好测试,测试能跑通的数据验证过了就好
|
46
akira 2016-11-22 17:26:53 +08:00
不用外键
少用存过 少用特定数据库特定版本才有的特性 |
47
z5864703 2016-11-22 17:54:04 +08:00
为什么一定要用数据库的外键,改动还麻烦。
在程序业务逻辑中实现外键约束,可以灵活很多 |
48
tonghuashuai 2016-11-22 18:03:51 +08:00
除了在大学里用过外键,实际工作中在设计数据库结构的时候都不约而同的不适用外键(一个眼神就不约而同了),外键的好处可能有很多,但是外键的不好你遇到过几次就会彻底抛弃它,改用逻辑约束。
|
49
Felldeadbird 2016-11-22 18:05:50 +08:00 via iPhone
用过外键,然后测试时发现数据特别麻烦,而且又不是一定要严格的模式,就没理他了
|
50
jianghu52 2016-11-22 18:42:00 +08:00
当初我们设计新系统的时候,还就要不要外键做过争论。我们的项目是 PHP+mysql 。
开始我属于强烈要求用外键的。在不考虑分库的情况下,外键的制约可以有效的让项目人员不用测那些奇葩的数据。(即业务之内不可能出现的那种)。另外就是,外键的存在在很大程度上让我们设计库的时候尽可能的多思考项目扩展的问题,如果没有外键的话,很多时候设计数据库就是有什么业务就设计成什么样子,很少考虑后期业务增加的时候,数据库是否容易扩展的问题。事实上因为我们最后选择了无外键的数据库设计,这些问题在后期都爆发了。当然,最后我也没办法袖手旁观,该填的坑还是得填。 跟我针锋相对的一个同事他的观点也很容易理解,在要求开发速度的情况下,外键不仅是一个困扰,更加是一种折磨。为什么这么说,越是严谨的外键设计,你在做测试的时候前置工作越多,越浪费时间不说,很多时候当你只想测试某一个小部分的业务逻辑的时候,你不得不从头开始填入数据库的表,你吭哧吭哧的花了半小时填了 5 个表,仅仅只为了测试其中的一个字段。这种花费在我们那时看来确实有点太大了。 另外外键还有一个隐性的成本,就是教学成本。这是我的一点经历。以前有个项目进了一个新人,我负责给他培训。当时的业务还不算复杂,我给他讲的时候,对外键讲的也不是很细,就告诉他如果表有这样的字段的话,那么程序会如何如何显示。结果等他下课自己做的时候,就发现动不动就是这个表插不进去,然后就要去看关联的外键是什么,之后又一堆找关联的表的字段信息,该插入什么样的数据。 往往一小段程序,会关联出很大一块儿的业务,你给他讲吧。他也不能马上就全记住,浪费时间。不给他讲吧,你就得给他做好前置的数据准备工作,也得花时间。 我到现在决定,外键适合那种不太大的业务,配上严谨的文档,严格的开发流程的团队。可惜我现在做的项目,只跟项目大小沾边,后面几个条件完全无法满足。 |
51
zhouquanbest 2016-11-22 19:04:01 +08:00
我的理解是 数据库只做“存储” 因此外键的逻辑 就应该放到代码里
没了外键 你发现分布式啥的都舒服了 天下太平 没了外键 你发现重构成本降低了 再也不怕 PM 改改改 没了外键 你发现如果每张表都设计了冗余字段 把外键提供的信息多存一份逻辑反而更简单性能更好了 |
52
cjyang1128 2016-11-22 19:06:58 +08:00
对于大的系统来说,能不用外键就不用外键,全靠代码逻辑,好定位 BUG !!!
|
53
codeyung 2016-11-22 19:08:55 +08:00
目前做的 没有在用外键 都是逻辑 和 中间表 做的 - -
|
54
heww 2016-11-22 19:11:52 +08:00 via iPhone
看了上面好多人的项目一上来就是好几百亿的项目!
|
55
xiaowangge 2016-11-22 19:33:05 +08:00
游戏行业的路过,几乎连数据库( MySQL )都不在用的。
凡是能用 Memcached (腾讯云的 Memcached 的可落地永久存储),就不用 MySQL 。 |
56
anubiskong 2016-11-22 19:47:01 +08:00 3
其实是个很简单的道理
外键就是程序逻辑, 逻辑应该都放在一起, 以方便维护, 当然是和程序放在一起才合理, 更何况程序还有更高的灵活性 唯一例外的情况就是巨大的性能差异或者是代码维护方面的成本上的差异, 而这两个例外在外键这个简单需求上没有体现. 数据层面的东西层级越少越好, 关系越少越好, 必要时为了性能适当的冗余数据, 用硬盘换性能 很多系统在设计上有历史包袱, 有些陈旧的东西不应该再想太多, 尤其是 php 和 mysql 这种很普及但上年纪的产品 |
57
hheedat 2016-11-22 19:47:31 +08:00
DBA 不让用, 666
|
58
movtoy 2016-11-22 20:22:55 +08:00
根据自身情况呗,
人家阿里有公关部,我开个豆腐店也要么 |
59
lightening 2016-11-22 20:32:23 +08:00
@SpicyCat 我想是指在 ORM (你的代码) 里实现,而不用数据库内置的外键约束。
|
60
lightening 2016-11-22 20:38:41 +08:00
我个人来说,是因为 ORM 本来就提供了约束,而且使用非常方便。没有必要的理由的话,我没必要加外键约束。
|
61
bomb77 2016-11-22 20:44:03 +08:00
233 ,原来大家都不用外键
|
62
echo111222 2016-11-22 20:47:08 +08:00 via Android
。。。。我记得我今年五月的时候,老师给我看看论文。。强行给我的数据库表加外键
|
63
xiaoxiaoming 2016-11-22 20:52:15 +08:00
做的都是互联网相关的业务.
从来没有用外键,都是靠业务逻辑区约束. |
64
abcbuzhiming 2016-11-22 21:39:06 +08:00
首先要理解当年数据库为什么要设计这些,外键说白了就是为了维护数据强一致性的。当年的数据库在金融领域应用最多最早,所以强一致性要求很高。而现在绝大部分公司的业务,哪需要这么高的强一致性啊
|
65
abcbuzhiming 2016-11-22 21:41:54 +08:00
@jianghu52 你的项目用了 PHP 就说明你们的项目试图快速成型而不是打算长期维护的严谨系统,这种系统费精力去用外键是给自己找不愉快,浪费在开发上的时间比你不用外键而需要填坑的时间还要长。不用外键本质是因为现在绝大部分业务压根就不需要那么严谨的一致性了。这玩意是有代价的
|
66
icegreen 2016-11-22 21:45:18 +08:00
从来不用, 逻辑全部在代码中体现, 便于维护.
你的问题是为什么抛弃了外键; 不妨从另一个角度想想,为什么用外键. 把约束从代码转移到数据库, 有什么好处. |
67
odirus 2016-11-22 21:49:18 +08:00 via Android
如果没有专业的 DBA ,还是少用
|
68
aldenchang 2016-11-22 22:09:20 +08:00
对于业务规则不够完善,经常需要工程师手动调整数据的库来说,外键简直是灾难。
|
69
jjx 2016-11-22 22:25:44 +08:00 1
看了这个讨论让人瞠目结舌, 虽说互联网应用对数据一致性要求不高, 但看到如此漠视, 所以这可能才是企业软件招不到合适的人的原因了
|
70
eyp82 2016-11-22 22:29:30 +08:00 2
数据库其实也从来没有要求大家用外键, 用外键只是为了满足某个范式的要求而已, 实际上范式根据严格程度不同有多重, 你甚至连第一范式都可以不满足(比如时下流行的 KV 数据库). 这并不是说数据库理论脱离实际.
所有的设计都是根据实际情况做各方面的妥协的, 比如企业应用(金融电信制造等等), 业务逻辑复杂但是并发量**相对**不高, 以前一般倾向于 scale-up, 不会做分库分表, 这样通过外键, trigger 等确保逻辑一致性是很合适的做法, 反之通过业务逻辑重复造轮子就不科学了. 互联网应用更多的是高并发, 单个业务逻辑本身一般并不复杂, 这样在业务逻辑内重复实现一个约束相对简单, 而由于分布式的要求, 外键是一个严重的阻碍, 所以不适用外键也在情理之中. 另外不同意楼上有人说的数据库的外键消耗更多系统资源的说法, 实际上数据库的外键经过这么多年的优化, 绝大多数情况下比业务代码里对关系数据库半懂不懂的人写的约束更高效. 你自己写的约束其实只是让你后面自己折腾起来更灵活而已. |
71
icegreen 2016-11-22 22:56:46 +08:00
没有外键也还有事务, 放弃了外键 不等于 放弃了一致性.
不要歪题 |
72
iyaozhen 2016-11-22 23:14:21 +08:00 via Android
关键是不方便维护,其它都是次要的。后面接手的人容易搞出事故。不要说外键了,字段不能为 null 的限制都能出问题。存储过程什么的也一样,好多时候不知道别人设置了这些,容易踩坑。
不过更多的是业务不需要,没需求、没收益。使用上主要矛盾集中在索引优化、主从一致等,哪有精力倒腾外键。 |
73
IwfWcf 2016-11-22 23:16:00 +08:00
最主要的原因就是为了可扩展性吧,未来可能需要分表
|
74
colatin 2016-11-22 23:31:25 +08:00
关键楼上都是搞分布式大系统的
|
75
yeqiu 2016-11-22 23:45:23 +08:00 1
@anubiskong
完全同意你的说法,关系就是逻辑,逻辑放在程序里封装成变化,天经地义。 这里有一个大写的但是 建立外键和代码实现外键哪个是确保数据一致性的最低成本的方法? 我想这个问题的结果无疑是建立外键。 那么有没有一种既能使用外键的低成本,又能很好的封装变化的方式呢? 当然有, entity framework 的 code first 就是我知道一种方式,按理说所有的 orm 框架应该都能干这事 |
76
skywatcher 2016-11-22 23:48:22 +08:00
我们 DBA 团队的也强烈建议不要外键,数据库同步、迁移、分表、分库时各种约束很麻烦。之前一次想同步一个表的数据到 beta 环境,各种外键约束,最后基本整个库都快被同步下来了,我特么只要一个表而已😠(别问为啥不直接 dump ,线上数据库规定不允许 dump 影响业务)
|
77
guyskk 2016-11-23 00:02:22 +08:00 via Android
@ljk 软删除,一定时间后彻底删除。企业需要应对比如盗号,用户误删数据,管理员误操作,以及司法取证这些问题。
|
78
exoticknight 2016-11-23 00:04:06 +08:00
请教一下不用外键如何用业务逻辑区约束?
|
79
wind3110991 2016-11-23 00:13:39 +08:00 3
外键这个东西和触发器是一样的道理,平时工作上省事了,一旦出了问题,定位半天找不到原因;
举个最简单的例子: 假设有 学生表 Student 和 班级表 Class 学生表中关联了一个外键,字段是班级的 id , 现在系统里有个学生 A 读了四年大学,所在班级为 B ,准备读研,读研就要换到班级 C 中,这个关联的班级 id 就要更新对吧; 但是这个时候假设学校教务系统不保留毕业班级的信息,会把毕业班级信息清除或者删除掉。这个时候就删掉了这个学生原来的班级 B (把班级表里 B 班级整条记录都删除了)。 A 学生到了研究生注册那天,去到报名处修改班级信息。这时候修改这个自己的班级时就会发现 DB 外键关联报错,而且不是代码逻辑的错误。 讲了半天。。就是让你,多写两行代码关联逻辑,少操份定位时想死的心。。 |
80
wind3110991 2016-11-23 00:15:00 +08:00
@exoticknight 新建并维护一个 map 表 C ,把 A 和 B 的关联记录到 C 中
|
81
duola 2016-11-23 00:50:53 +08:00
@wind3110991 这是不是所说的 中间表 呢?
|
82
feather12315 2016-11-23 01:04:29 +08:00 via Android
@wind3110991 这是数据库设计的问题,删除异常。并不是外键的锅
|
83
vibbow 2016-11-23 01:31:24 +08:00
懒
|
84
ifhwhlwpto 2016-11-23 03:09:02 +08:00
@sunsh217 跟中国的教科书有什么关系,国外的教科书难道就不教你规范?
|
85
beidouxun 2016-11-23 03:39:40 +08:00 via Android
@wind3110991 删除 b 班级肯定把关联此班级学生表的外键清空!等待分配班级!程序的问题!
|
86
q397064399 2016-11-23 05:41:46 +08:00
@wind3110991 赞
|
88
ebony0319 2016-11-23 08:20:50 +08:00
我觉得外键大多数存在于教科书。实际项目中比较少(基本上不用)外键,主要是表多了不好维护。用一定的存储过程,一定的视图等。很少在数据库中(至少在我见到的十几万行 sql 中)使用控制流语句。数据库不适合干复杂的事情。
|
89
sunsh217 2016-11-23 09:06:25 +08:00 via Android
@ifhwhlwpto 国外就没有教科书一说
|
90
ren2881971 2016-11-23 09:08:08 +08:00
因为 drop 的时候太麻烦。。。
|
91
wupher 2016-11-23 09:09:11 +08:00
除非小项目,基本不使用外键。外键关系使用程序代码来维护,在数据库中不体现。
后期如果需要数据清理,数据调整时,有外键关系(尤其是复杂的外键关系)会造成表数据非常难以修改、维护。 |
92
bi168 2016-11-23 09:58:32 +08:00 via Android
外键确实在实际生产环境中比较少用,一般都在程序中实现逻辑 避免维护修改数据的复杂性
|
93
barbery 2016-11-23 10:02:18 +08:00
唯品会的确是不允许外键, db 变更如果有外键的话, 99.9%是要被驳回的。。。原因的话,我猜想是互联网企业,性能优先,使用物理外键的话,以后分表分库都会有问题。
|
94
iyangyuan 2016-11-23 10:03:12 +08:00 1
逻辑层面统一,物理层面分离,保障以后的可伸缩性
|
95
bi168 2016-11-23 10:11:12 +08:00
确实一方面给性能带来负担,一方面在交易系统中,删除数据就会自动删除外键数据,这个也会经常出现问题。
关于数据库,数据仓库、大数据、数据分析的问题,我这有个社区 www.bi168.cn 欢迎大家多多交流 |
96
nandaye 2016-11-23 10:13:01 +08:00
有的主键都不要得,不奇怪,看你的应用场景了
|
97
blacklee 2016-11-23 12:05:07 +08:00
在 2016 年这个节点,我觉得更早的 2006 年就差不多可以这么说了:
数据库使用外键,属于教条主义! |
98
fortunezhang 2016-11-23 13:46:30 +08:00
从来没有用过外键:)
|
99
killerv 2016-11-23 14:03:43 +08:00
我觉的业务逻辑最好还是写在代码中,外键约束有时候不够灵活,排查问题也不方便
|