Onde você deve definir chaves estrangeiras?

Respostas:

41

Coloque as chaves estrangeiras no banco de dados. Mesmo que você valide os dados no aplicativo antes de salvá-los, os FKs são um bom backup de controle de qualidade. Para uma primeira aproximação, os aplicativos sempre apresentam problemas de dados. Deixar controles como este fora do sistema apenas convida a modos de falha em que os dados são corrompidos silenciosamente.

Não há nada como trabalhar no data warehousing por alguns anos para ver isso em ação. Você gasta seu tempo juntando os pedaços após erros ilegais dos desenvolvedores de aplicativos que pensavam que poderiam impor a integridade dos dados no código do aplicativo. Passe algum tempo fazendo isso e você concluirá que a integridade dos dados gerenciados por aplicativos é pouco mais do que uma presunção.

Além disso, o otimizador de consulta pode usar chaves estrangeiras para inferir coisas sobre junções de tabelas, de modo que os FK resultarão em planos de consulta mais eficientes.

Existem muitos outros benefícios para chaves estrangeiras também. Faça um favor a todos - coloque os FKs no banco de dados.

ConcernedOfTunbridgeWells
fonte
15

A integridade referencial deve ser tratada no nível mais baixo possível, que seria o banco de dados subjacente. Os sistemas de gerenciamento de banco de dados relacional são otimizados para lidar com isso. Não faz sentido reinventar a roda proverbial.

É aceitável definir lógica de domínio no código do aplicativo para impedir que a instrução DML cause uma exceção de RI, mas isso não deve ser visto como um substituto para os relacionamentos de chave estrangeira no banco de dados.

Thomas Stringer
fonte
12

Eu vou sair do ramo aqui, esperando que isso seja votado para baixo, já que este é um grupo focado no DBA.

Concordo que o uso de chaves estrangeiras estritas é a melhor decisão na maioria dos cenários. No entanto, existem alguns casos em que chaves estrangeiras causam mais problemas do que resolvem.

Quando você lida com um ambiente altamente concorrente, como um aplicativo Web de alto tráfego, e usa um ORM robusto e bem estabelecido, as chaves estrangeiras podem causar problemas de bloqueio que dificultam o dimensionamento e a manutenção de um servidor. Ao atualizar linhas em uma tabela filha, a linha pai também está bloqueada. Em muitos cenários, isso pode limitar drasticamente a simultaneidade devido à contenção de bloqueio. Além disso, às vezes você precisa executar manutenção em tabelas individuais, como processos de arquivamento, nos quais pode ser necessário (intencionalmente) violar regras de integridade referencial, pelo menos temporariamente. Com as chaves estrangeiras instaladas, isso pode ser incrivelmente difícil e, em alguns RDBMSs, desabilitar as restrições de chave estrangeira causará uma reconstrução da tabela, um processo demorado que pode exigir um tempo de inatividade substancial.

Entenda que estou incluindo a ressalva de que você deve usar uma estrutura robusta capaz de entender a integridade referencial externa ao banco de dados. Ainda assim, você provavelmente acabará com alguns problemas de integridade referencial. No entanto, existem muitos casos em que simplesmente não é grande coisa ter linhas órfãs ou pequenas violações de integridade referencial. Eu argumentaria que a maioria dos aplicativos da web se enquadra nessa categoria.

Dito isto, ninguém começa como o Facebook. Comece definindo chaves estrangeiras no seu banco de dados. Monitor. Se você acabar tendo problemas, entenda que pode ser necessário reduzir algumas dessas restrições à escala.

Conclusão: a maioria dos bancos de dados deve ter chaves estrangeiras. Ambientes altamente simultâneos podem ser melhores sem chaves estrangeiras. Se você chegar a esse ponto, poderá considerar a possibilidade de eliminar essas restrições.

Eu vou vestir meu traje retardante de chamas agora.

EDIT 2012-03-23 ​​07:00

Ao pensar nas consequências do bloqueio de chaves estrangeiras, deixei de mencionar o custo de todas as pesquisas de linhas adicionais que são implicitamente geradas internamente, aumentando a carga do servidor.

Por fim, meu argumento é que as chaves estrangeiras não são gratuitas. Em muitos casos, o custo vale a pena, mas há cenários em que esse custo excede seu benefício.

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

Sejamos concretos. Estou escolhendo o MySQL / InnoDB neste exemplo, que não é altamente respeitado por seu comportamento de chave estrangeira, mas é com o que eu estou mais familiarizado e provavelmente o banco de dados da web mais usado. Não tenho certeza de que outro banco de dados se sairia melhor com o exemplo que estou prestes a mostrar.

Considere uma tabela filha com uma chave estrangeira referenciando o pai. Como um exemplo, consulte as tabelas film e film_actor no banco de dados de amostra sakila no 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

A restrição relevante é film_actor (fk_film_actor_film) para o meu exemplo.

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

Observe que não foi possível atualizar um campo não relacionado na linha pai ao inserir na tabela filho. Isso acontece porque o InnoDB está mantendo um bloqueio compartilhado na linha em que film.film_id = 508 devido à restrição FK no film_actor, portanto, o UPDATE para essa linha não pode obter o bloqueio exclusivo necessário. Se você reverter essa operação e executar o UPDATE primeiro, você terá o mesmo comportamento, mas o INSERT está bloqueado.

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

Considere uma userstabela em um aplicativo Web em que geralmente há dezenas de tabelas relacionadas. Essencialmente, qualquer operação em qualquer linha relacionada impede uma atualização na linha pai. Esse pode ser um problema desafiador quando você tem vários relacionamentos de chave estrangeira e muita concorrência.

As restrições do FK também podem dificultar as soluções alternativas para a manutenção da tabela. Peter Zaitsev, da Percona, tem um post sobre isso que explica melhor do que eu: Seqüestro de chaves estrangeiras do Innodb .

Aaron Brown
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Paul White diz GoFundMonica
6

É uma boa prática usar chave estrangeira no banco de dados. Isso ajuda-

  • para manter a integridade dos dados, removendo a possibilidade de dados indesejados
  • para aumentar o desempenho. Em sistemas que indexam automaticamente campos, as referências de chave estrangeira podem aumentar o desempenho
  • para escrever menos código pelo programador. tipo, usandoON DELETE CASCADE
Abdul Ahad
fonte