您应该在哪里定义外键?


Answers:


41

将外键放在数据库上。即使在保存应用程序之前验证了数据,FK也是很好的质量检查备份。首先,应用程序始终会出现数据问题。将这样的控件留在系统之外只会引发故障模式,在这种模式下数据会被静默破坏。

几年来没有什么比在数据仓库中工作更实际的了。在应用程序开发人员犯了点头错误之后,您就花费了很多时间来整理这些碎片,他们认为应用程序开发人员可以在应用程序代码中实现数据完整性。花任何时间进行操作,您将得出结论,由应用程序管理的数据完整性仅是一种假设。

此外,查询优化器可以使用外键来推断有关表联接的内容,因此FK将导致更有效的查询计划。

外键还有很多其他好处。大家帮个忙-将FK放在数据库中。


15

引用完整性应在最低级别上进行处理,这将是基础数据库。关系数据库管理系统经过优化可处理此问题。重新发明众所周知的轮子没有任何意义。

在应用程序代码中定义域逻辑以防止DML语句甚至导致RI异常是可以接受的,但是这不应被视为数据库中外键关系的替代。


12

由于这是一个专注于DBA的小组,因此我将全力以赴。

我同意在大多数情况下使用严格的外键是最好的决定。但是,在某些情况下,外键引起的问题超出了解决的范围。

当您处理非常高的并发环境(例如,高流量的Web应用程序)并使用完善的,健壮的ORM时,外键会引起锁定问题,从而使扩展和维护服务器变得困难。当更新子表中的行时,父行也被锁定。在许多情况下,由于锁定争用,这可能会大大限制并发性。此外,有时您必须对单个表执行维护,例如归档过程,在该过程中,您可能需要(有意)至少暂时性地破坏参照完整性规则。有了外键,这将变得异常困难,并且在某些RDBMS中,禁用外键约束将导致表的重建,这是一个耗时的过程,可能需要大量的停机时间。

请理解,我在此警告您必须使用健壮的框架,该框架必须能够理解数据库外部的参照完整性。不过,您最终可能会遇到一些参照完整性问题。但是,在许多情况下,只有孤立的行或较小的参照完整性冲突并没有什么大不了的。 我认为大多数Web应用程序都属于此类。

话虽这么说,没有人像Facebook这样开始。首先在数据库中定义外键。监控。如果最终遇到问题,请了解您可能需要放下一些约束以进行扩展。

结论:大多数数据库应具有外键。没有外键,高度并发的环境可能会更好。如果达到这一点,则可能需要考虑删除这些约束。

我现在去穿我的阻燃服。

编辑2012-03-23 7:00 AM

在考虑外键的锁定后果时,我忽略了在内部隐式生成的所有其他行查找的开销,这增加了服务器负载。

最终,我的观点是外键不是免费的。在许多情况下,成本是值得的,但是在某些情况下,成本超出了其收益。

编辑2012-03-23 7:38 AM

让我们具体一点。在此示例中,我选择的是MySQL / InnoDB,该示例因其外键行为而未得到高度推崇,但这是我最熟悉的,并且可能是最常用的Web数据库。我不确定其他数据库在我将要展示的示例中表现会更好。

考虑一个带有外键引用父表的子表。例如,请参见MySQL的sakila示例数据库中的film和film_actor表:

CREATE TABLE `film` (
  `film_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `description` text,
  `release_year` year(4) DEFAULT NULL,
  `language_id` tinyint(3) unsigned NOT NULL,
  `original_language_id` tinyint(3) unsigned DEFAULT NULL,
  `rental_duration` tinyint(3) unsigned NOT NULL DEFAULT '3',
  `rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
  `length` smallint(5) unsigned DEFAULT NULL,
  `replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
  `rating` enum('G','PG','PG-13','R','NC-17') DEFAULT 'G',
  `special_features` set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`film_id`),
  KEY `idx_title` (`title`),
  KEY `idx_fk_language_id` (`language_id`),
  KEY `idx_fk_original_language_id` (`original_language_id`),
  CONSTRAINT `fk_film_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_film_language_original` FOREIGN KEY (`original_language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8

CREATE TABLE `film_actor` (
  `actor_id` smallint(5) unsigned NOT NULL,
  `film_id` smallint(5) unsigned NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`actor_id`,`film_id`),
  KEY `idx_fk_film_id` (`film_id`),
  CONSTRAINT `fk_film_actor_actor` FOREIGN KEY (`actor_id`) REFERENCES `actor` (`actor_id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_film_actor_film` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

在我的示例中,相关的约束是film_actor(fk_film_actor_film)。

session1> BEGIN;
session1> INSERT INTO film_actor (actor_id, film_id) VALUES (156, 508);
Query OK, 1 row affected (0.00 sec)

session2> BEGIN;
session2> UPDATE film SET release_year = 2005 WHERE film_id = 508;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

请注意,插入子表时,我无法更新父行中的不相关字段。发生这种情况的原因是,由于film_actor的FK约束,InnoDB在film.film_id = 508的行上持有共享锁,因此对该行的UPDATE无法获得所需的排他锁。如果您撤消该操作并首先运行UPDATE,则您的行为相同,但是INSERT被阻止。

session1> BEGIN;
session1> UPDATE film SET release_year = 2005 WHERE film_id = 508;
Query OK, 1 row affected (0.00 sec)

session2> BEGIN;
session2> INSERT INTO film_actor (actor_id, film_id) VALUES (156, 508);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

考虑一个usersWeb应用程序中的表,其中通常有数十个相关表。基本上,对任何相关行的任何操作都会阻止对父行的更新。当您具有多个外键关系和大量并发时,这可能是一个具有挑战性的问题。

FK约束也会使表维护的变通办法也面临挑战。Percona的Peter Zaitsev有一篇关于此的博客文章,比我能更好地解释它:劫持Innodb外键


评论不作进一步讨论;此对话已转移至聊天
保罗·怀特说GoFundMonica

6

优良作法是在数据库中使用外键。它有助于-

  • 通过消除不必要数据的可能性来保持数据完整性
  • 以提高性能。在具有自动索引字段的系统中,外键引用可以提高性能
  • 减少程序员编写的代码。喜欢,使用ON DELETE CASCADE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.