copy:为什么不建议数据库建外键

导语

有人可能在面试的时候被问到过,你觉得为什么不建议数据库通过构建外键做约束?你可以回答说,在《阿里Java开发手册》中是这样说的:不得使用外键与级联,一切外键概念必须在应用层解决。而且这个规约是强制的。但是我想这样的答案,可能并不能让面试官满意。当然,可能也有人说:在每次对表数据进行操作时,需要考虑外键的因素,太过于繁琐,但是这依然不是问题的重点。

那么该如何回答这样的一个问题呢?下面我们就来进行简单的说明。

外键的作用(原理)与优点

其实外键也不是一无是处,外键主要是用于完整性的约束的检查。这句怎么解释呢?

  • 首先已经通过外键把数据间的关系构建起来,减少了代码的构建工作
  • 严格保证数据的完整性

不过这些理由并不是推荐使用外键的理由,可能有人说,那你说说建外键的缺点呗?这里先不说它的缺点,我们来以一个简单的例子来进行体现。

在Mysql的InnoDB存储引擎中,在一个外键列中,如果你没有显示的给此列加索引的话,InnoDB存储引擎会自己给这一列加上一个索引,数据库这么处理的原因,是因为这样可以避免表锁。如果InnoDB存储引擎不自动添加索引,在进行数据操作时可能会出现死锁,Oracle中就存在这样的问题(不知道新版本的Oracle是否改善了这个问题),这其实是一个并发问题。

简单的说了下,在InnoDB中建外键是,InnoDB自己进行的一些优化。那么,现在来说下在有外键的情况下会出现什么样的状况。

外键测试
时间会话A会话B
1begin 
2delete from student where id = 7; 
3 begin
4 

insert into grade selete * from student id in (6,7,8);

#由于其中有外键,执行时会被阻塞

(waiting)

在这个例子中,可以看到当在两个会话都没有进行事务的commit或rollback时,会话B的操作会被阻塞,因为id等于7的student表中已经加了一个X锁(排它锁),此时会话B中又需要对student表中id等于7的这条数据行加了一个S锁(共享锁),这样Insert操作便会被阻塞。

此时加入要读student表的数据的话,同时使用的还是一致性非锁定读,但是这样会话B会读到student等于7的数据,便会认为这条数据是可操作性的,但当会话A的事务提交后,这条数据便不存在了,这样就会导致数据不一致的情况。

从上面的例子中,我们可以看出来大概有这样几个问题:性能问题、并发问题(数据一致性),当然你除了这些问题,还有数据库的扩展问题。对于这几个问题,我们来进行一些简单的说明。

性能问题

以上面的例子为例,当程序每次往grade表中插入数据的时候,就必须去到student表中去校验这所要插入的数据是否存在,但是当把这个工作交给代码层去操作时,这些校验就可以有我们自己去做了,况且并不是所有的情况都需要去校验有‘外键’关联的表数据。

并发问题

在上面的例子中,我们说到了,当我们在开启会话A的时候,就已经在会话A中加了X锁,当在会话B中操作字表的数据时,又会在student表中id等于7的这条数据上加了一个S锁,如果会话A一直不提交会出现什么样的情况?这也就是一种资源竞争,如果会话A的事务提交了,又会导致数据的不一致。因此这也影响数据的插入性能。

扩展问题

在有外键的数据库中是不适做合分布式、高并发集群的,首先在水平分库的时候,外键是无法生效的。所以把这样的关系约束交给应用层去维护的话,会给后面的工作省去一些麻烦的。还有我们上面也说了Mysql与Oracle在外键上的差别,加入你把带有外键的MySQL数据库迁移到了Oracle中,那么会有什么情况呢?

 

总结

笔者写这篇文章,是因为最近在项目中发现有小伙伴在大量的使用外键,第一在维护表的时候,工作量很大,第二在性能上明显有损耗。因此想着写一篇文章,毕竟笔者使用外键还是当年刚学编程的时候。

 

另:不使用外键的9理由

copy:https://blog.csdn.net/dome_/article/details/99714400?biz_id=102&utm_term=%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E4%BD%BF%E7%94%A8%E5%A4%96%E9%94%AE&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-5-99714400&spm=1018.2118.3001.4449

 

Piotr Kononow是一位业务分析师、软件架构师和项目经理,他拥有15年以上编程经验和背景(SQL,java,C++…)。他的专长是数据仓库/ BI和商业应用,这是他的一篇文章:

最近我和几位DBA和架构师争论,他们对一些数据库没有外键感到震惊,并声称这是一种设计缺陷,是不应该发生的。如果发生必须马上改正。我想与他们争辩。我的经验告诉我,很多数据库(大多数我曾经使用的)不包含外键时并不总是一件坏事。在这篇文章中,我想把重点放在为什么的原因上。

为什么这是一个问题?

1. 潜在的数据完整性问题,

缺少外键明显问题是数据库不能强制进行引用完整性检查,如果在高一层没有正确处理,则可能会导致数据不一致(子行没有相应父行)。

2. 表格关系不清晰

数据库中缺少外键的另一个不太明显的负面影响是,不了解该模式的人很难找到正确的表并找出表关系。这可能会导致严重的数据库查询和报告问题。

为什么数据库可以没有外键?

让我们来看看数据库可以没有外键的原因。首先一个简短的免责声明(因为文章引发了一些关于LinkedIn群体的争议):

下面的理由绝不鼓励不要在数据库中使用外键约束。这仅仅是我在各种渠道(主要是互联网论坛)都能找到的许多开发人员、架构师为什么不使用它们的理由。我个人(和许多其他经验丰富的数据库专家)建议在任何可能的地方使用它们(不会导致更多的问题)。

1. 性能

在表上拥有活动的外键可以提高数据质量,但会影响插入、更新和删除操作的性能。在这些任务之前,数据库需要检查它是否违反数据完整性。这就是为什么一些架构师和DBA完全放弃外键的原因。数据仓库和分析数据库尤其如此,这些数据仓库和分析数据库不以交易方式(一次一行)处理数据,而是批量处理数据。性能是数据仓库和商业智能的一切。

2. 传统数据

许多数据库在设计时需要存储来自旧数据库和遗留数据,这些数据可能对数据质量和完整性没有那么严格。为了能够容纳旧的脏数据,架构师可以选择a)清理和转换遗留数据(昂贵的练习),或者b)放弃在数据库级别上强制执行参照完整性。一些打包的ERP和CRM应用程序也使用这种方法。

3. 全表重新加载

一些数据库,如数据仓库,分段或接口数据库,需要经常从外部重新加载数据。这会导致重新加载时数据不一致(在父表为空的情况下,子表可能已满载)。这可以通过在重新加载时禁用外键来绕过。然而,这引入了额外的逻辑和复杂性以及另一个失败点。如上所述,对性能有负面影响。通常,成本大于收益,开发人员不用担心外键。

4. 更高层次的框架

一些应用程序使用编程框架,在物理数据库之上创建另一个逻辑层。开发人员不使用插入或更新语句来修改数据,而使用API或者框架在后台执行所有操作。ORM(对象关系映射)框架或Ruby on Rails框架就是这种情况。这些工具负责参照完整性,并与RDBMS一起创建更高级别的数据库引擎。这些框架可以自己创建数据库表,而不总是创建外键。使用这些工具的开发人员很少会干扰自动生成的模式,并且不需要外键。

5. 跨数据库关系

这可能不是数据库没有外键的正确理由,一些数据库跨越更多的物理数据库甚至引擎,并且在技术上可能不能创建跨越数据库的它不能在同一台服务器上的两个数据库上创建key。SQL Server就是一个很好的例子 - 它不能在同一台服务器上的两个数据库上创建key。而且这种架构在大型系统中很常见。

6. 数据库平台不可知论者

类似于前一个,一些应用程序被设计为数据库平台(DBMS)不可知的,并能够在Oracle,SQL Server,DB / 2或Sybase等各种数据库上工作。这是我读过的有关PeopleSoft(目前由Oracle拥有)的内容。设计人员不想绑定到任何特定的平台,并将所有逻辑推送到应用程序层,尽可能清楚地离开数据库层。

7. 对更改开放

我与Oracle一直保持紧密联系,我听说过另一个关于其应用程序的故事,这是Oracle自己的产品 - Oracle电子商务套件 - 就是它被设计成尽可能定制。Oracle提供了坚实的基础,使实施团队具有弹性,可以尽可能多地决定设计。至少这是他们所说的。也许这个原因和以前一样,或者是下一个原因:

8. 懒惰的架构师

在创建数据库时,如果要存储数据,则需要创建一些表和列。这是最低限度。但是,您不必创建保持数据一致性的结构,如主键,唯一键,外键或约束。这需要一些努力,但是却没有带来直接的好处。一些架构师和数据库管理员只是忽略了这一部分。

9. 保持模型的秘密

也许这是一个很遥远的问题,但也许有时候是因为人们不希望别人知道太多太容易。一般来说,人们希望被需要和不可替代。一个完美的自我解释的设计可能会使他们过时。但这只是我的理论。

 

参考:

1.https://blog.csdn.net/zfy163520/article/details/104009835?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160466314619724838508638%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160466314619724838508638&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-1-104009835.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AE%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%96%E9%94%AE&spm=1018.2118.3001.4449

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值