Est-il préférable de définir des clés étrangères dans la base de données ou dans la partie code d'une application?
Est-il préférable de définir des clés étrangères dans la base de données ou dans la partie code d'une application?
Réponses:
Mettez les clés étrangères dans la base de données. Même si vous validez les données dans l'application avant de les enregistrer, les FK sont une bonne sauvegarde QA pièce. Pour une première approximation, les applications ont toujours des problèmes de données. Laisser des contrôles comme celui-ci hors du système invite simplement les modes de défaillance où les données sont corrompues en silence.
Rien de tel que de travailler dans l'entrepôt de données pendant quelques années pour voir cela en action. Vous passez votre temps à ramasser les morceaux après des erreurs de tête de la part de développeurs d'applications qui pensaient pouvoir appliquer l'intégrité des données dans le code de l'application. Passez du temps à le faire et vous conclurez que l'intégrité des données gérées par l'application n'est guère plus qu'une vanité.
De plus, l'optimiseur de requêtes peut utiliser des clés étrangères pour déduire des informations sur les jointures de table, de sorte que les FK se traduiront par des plans de requête plus efficaces.
Les clés étrangères offrent également de nombreux autres avantages. Faites plaisir à tout le monde - mettez les FK dans la base de données.
L'intégrité référentielle doit être gérée au niveau le plus bas possible, qui serait la base de données sous-jacente. Les systèmes de gestion de bases de données relationnelles sont optimisés pour gérer cela. Cela n'a pas de sens de réinventer la roue proverbiale.
Il est acceptable de définir la logique du domaine dans le code d'application pour éviter que l'instruction DML ne provoque même une exception RI, mais cela ne doit pas être considéré comme un remplacement des relations de clé étrangère dans la base de données.
Je vais sortir sur une branche ici en m'attendant à ce que cela soit rejeté, car il s'agit d'un groupe axé sur le DBA.
Je suis d'accord que l'utilisation de clés étrangères strictes est la meilleure décision dans la plupart des scénarios. Cependant, dans certains cas, les clés étrangères causent plus de problèmes qu'elles n'en résolvent.
Lorsque vous traitez avec un environnement très concurrentiel, comme une application Web à fort trafic, et que vous utilisez un ORM robuste et bien établi, les clés étrangères peuvent provoquer des problèmes de verrouillage qui rendent difficile la mise à l'échelle et la maintenance d'un serveur. Lors de la mise à jour des lignes d'une table enfant, la ligne parent est également verrouillée. Dans de nombreux scénarios, cela peut considérablement limiter la simultanéité en raison de conflits de verrouillage. De plus, vous devez parfois effectuer des opérations de maintenance sur des tables individuelles, telles que des processus d'archivage où vous devrez peut-être (intentionnellement) enfreindre les règles d'intégrité référentielle, au moins temporairement. Avec des clés étrangères en place, cela peut être incroyablement difficile et dans certains SGBDR, la désactivation des contraintes de clés étrangères entraînera une reconstruction de la table, un processus long qui peut nécessiter des temps d'arrêt substantiels.
Comprenez que j'inclus la mise en garde que vous devez utiliser un cadre robuste qui est capable de comprendre l'intégrité référentielle externe à la base de données. Pourtant, vous vous retrouverez probablement avec des problèmes d'intégrité référentielle. Cependant, il existe de nombreux cas où ce n'est pas si grave d'avoir des lignes orphelines ou des violations mineures de l'intégrité référentielle. Je dirais que la majorité des applications Web entrent dans cette catégorie.
Cela étant dit, personne ne commence comme Facebook. Commencez par définir des clés étrangères dans votre base de données. Moniteur. Si vous rencontrez des problèmes, sachez que vous devrez peut-être supprimer certaines de ces contraintes à l'échelle.
En conclusion: la plupart des bases de données doivent avoir des clés étrangères. Les environnements hautement simultanés pourraient être mieux sans clés étrangères. Si vous atteignez ce point, vous devrez peut-être envisager de supprimer ces contraintes.
Je vais maintenant enfiler mon costume ignifuge.
EDIT 2012-03-23 7:00 AM
En pensant aux conséquences de verrouillage des clés étrangères, j'ai négligé de mentionner le coût de toutes les recherches de lignes supplémentaires qui sont implicitement générées en interne, ce qui augmente la charge du serveur.
En fin de compte, mon point est que les clés étrangères ne sont pas gratuites. Dans de nombreux cas, le coût en vaut la peine, mais il existe des scénarios où ce coût dépasse leur avantage.
EDIT 2012-03-23 7:38 AM
Soyons concrets. Je choisis MySQL / InnoDB dans cet exemple, qui n'est pas très respecté pour son comportement de clé étrangère, mais c'est ce que je connais le plus et qui est probablement la base de données Web la plus utilisée. Je ne suis pas certain qu'une autre base de données s'en tirerait mieux avec l'exemple que je suis sur le point de montrer.
Considérons une table enfant avec une clé étrangère référençant le parent. À titre d'exemple, consultez les tables film et film_actor dans la base de données exemple sakila dans MySQL:
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
La contrainte pertinente est film_actor (fk_film_actor_film) pour mon exemple.
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
Notez que je n'ai pas pu mettre à jour un champ non lié dans la ligne parent lors de l'insertion dans la table enfant. Cela se produit car InnoDB détient un verrou partagé sur la ligne où film.film_id = 508 en raison de la contrainte FK sur film_actor, donc la MISE À JOUR de cette ligne ne peut pas obtenir le verrou exclusif requis. Si vous annulez cette opération et exécutez d'abord la MISE À JOUR, vous avez le même comportement, mais l'INSERT est bloqué.
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
Considérez une users
table dans une application Web où il y a souvent des dizaines de tables liées. Essentiellement, toute opération sur une ligne associée empêche une mise à jour de la ligne parent. Cela peut être un problème difficile lorsque vous avez plusieurs relations de clé étrangère et beaucoup de simultanéité.
Les contraintes FK peuvent également rendre difficiles les solutions de contournement pour la maintenance des tables. Peter Zaitsev de Percona a un blog à ce sujet qui l'explique mieux que moi: détournement de clés étrangères Innodb .
Il est recommandé d'utiliser une clé étrangère dans la base de données. Ça aide-
ON DELETE CASCADE