Dove dovresti definire le chiavi esterne?


Risposte:


41

Inserire le chiavi esterne nel database. Anche se si convalidano i dati nell'applicazione prima di salvarli, gli FK sono un buon backup QA. Per una prima approssimazione, le applicazioni presentano sempre problemi di dati. Lasciare i controlli di questo tipo fuori dal sistema invita solo le modalità di errore in cui i dati vengono danneggiati silenziosamente.

Non c'è niente come lavorare nel data warehousing per alcuni anni per vederlo in azione. Trascorri il tuo tempo a raccogliere i pezzi dopo piccoli errori da parte degli sviluppatori di applicazioni che pensavano di poter applicare l'integrità dei dati nel codice dell'applicazione. Trascorrere del tempo in questo modo e si concluderà che l'integrità dei dati gestiti dall'applicazione è poco più di una presunzione.

Inoltre, Query Optimizer può utilizzare chiavi esterne per dedurre elementi sui join di tabella, quindi gli FK comporteranno piani di query più efficienti.

Ci sono molti altri vantaggi anche per le chiavi esterne. Fai un favore a tutti: inserisci gli FK nel database.


15

L'integrità referenziale dovrebbe essere gestita al livello più basso possibile, che sarebbe il database sottostante. I sistemi di gestione di database relazionali sono ottimizzati per gestirlo. Non ha senso reinventare la proverbiale ruota.

È accettabile definire la logica di dominio nel codice dell'applicazione per evitare che l'istruzione DML provochi anche un'eccezione RI, ma ciò non dovrebbe essere visto come una sostituzione delle relazioni di chiave esterna nel database.


12

Ho intenzione di uscire su un arto qui, aspettandomi pienamente che questo venisse votato verso il basso poiché si tratta di un gruppo incentrato sulla DBA.

Concordo sul fatto che l'utilizzo di chiavi esterne rigorose sia la decisione migliore nella maggior parte degli scenari. Tuttavia, ci sono alcuni casi in cui le chiavi esterne causano più problemi di quanti ne risolvano.

Quando si ha a che fare con un ambiente altamente concorrenziale come un'applicazione web ad alto traffico e si utilizza un ORM solido e consolidato, le chiavi esterne possono causare problemi di blocco che rendono difficile il ridimensionamento e la manutenzione di un server. Quando si aggiornano le righe in una tabella figlio, anche la riga padre viene bloccata. In molti scenari, ciò può limitare drasticamente la concorrenza a causa del blocco della contesa. Inoltre, a volte è necessario eseguire la manutenzione su singole tabelle, come i processi di archiviazione in cui potrebbe essere necessario (intenzionalmente) infrangere le regole di integrità referenziale, almeno temporaneamente. Con le chiavi esterne in atto, questo può essere incredibilmente difficile e in alcuni RDBMS la disabilitazione dei vincoli di chiave esterna causerà una ricostruzione della tabella, un processo che richiede tempo e che può richiedere sostanziali tempi di inattività.

Capire che sto includendo l'avvertenza che è necessario utilizzare un framework solido in grado di comprendere l'integrità referenziale esterna al database. Tuttavia, probabilmente ti ritroverai con alcuni problemi di integrità referenziale. Tuttavia, ci sono molti casi in cui non è un grosso problema avere righe orfane o lievi violazioni dell'integrità referenziale. Direi che la maggior parte delle applicazioni web rientrano in questa categoria.

Detto questo, nessuno inizia come Facebook. Inizia definendo le chiavi esterne nel tuo database. Tenere sotto controllo. Se si finiscono per avere problemi, capire che potrebbe essere necessario eliminare alcuni di questi vincoli per ridimensionarli.

In conclusione: la maggior parte dei database dovrebbe avere chiavi esterne. Ambienti altamente concorrenti potrebbero essere meglio senza chiavi esterne. Se raggiungi quel punto, potresti dover considerare di eliminare quei vincoli.

Adesso vado a indossare la mia tuta ignifuga.

EDIT 2012-03-23 ​​7:00 AM

Nel pensare alle conseguenze del blocco delle chiavi esterne, ho trascurato di menzionare il costo di tutte le ricerche di riga aggiuntive generate implicitamente internamente, aggiungendo al carico del server.

In definitiva, il mio punto è che le chiavi esterne non sono gratuite. In molti casi, ne vale la pena, ma ci sono scenari in cui tale costo supera il loro vantaggio.

EDIT 2012-03-23 ​​7:38 AM

Siamo concreti. Sto scegliendo MySQL / InnoDB in questo esempio, che non è molto rispettato per il suo comportamento con chiave esterna, ma è ciò che conosco di più ed è probabilmente il database Web più comunemente usato. Non sono sicuro che altri database andrebbero meglio con l'esempio che sto per mostrare.

Si consideri una tabella figlio con una chiave esterna che fa riferimento al padre. Ad esempio, vedi le tabelle film e film_actor nel database di esempio sakila in 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

Il vincolo rilevante è film_actor (fk_film_actor_film) per il mio esempio.

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

Si noti che non sono stato in grado di aggiornare un campo non correlato nella riga padre durante l'inserimento nella tabella figlio. Ciò accade perché InnoDB è in possesso di un blocco condiviso sulla riga in cui film.film_id = 508 a causa del vincolo FK su film_actor, quindi l'AGGIORNAMENTO a quella riga non può ottenere il blocco esclusivo richiesto. Se si annulla tale operazione ed si esegue prima UPDATE, si ha lo stesso comportamento, ma INSERT è bloccato.

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

Prendi in considerazione una userstabella in un'applicazione Web in cui sono spesso presenti decine di tabelle correlate. In sostanza, qualsiasi operazione su una riga correlata impedisce un aggiornamento della riga padre. Questo può essere un problema difficile quando si hanno più relazioni con chiave esterna e molta concorrenza.

I vincoli FK possono rendere difficili anche le soluzioni alternative per la manutenzione della tabella. Peter Zaitsev di Percona ha un post sul blog che lo spiega meglio di me: dirottare le chiavi straniere di Innodb .


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Paul White dice GoFundMonica

6

È buona norma utilizzare la chiave esterna nel database. Aiuta-

  • mantenere l'integrità dei dati rimuovendo la possibilità di dati indesiderati
  • per aumentare le prestazioni. Nei sistemi che eseguono l'indicizzazione automatica dei campi, i riferimenti a chiave esterna possono migliorare le prestazioni
  • per scrivere meno codice dal programmatore. come, usandoON DELETE CASCADE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.