외래 키를 어디에 정의해야합니까?


답변:


41

데이터베이스에 외래 키를 넣습니다. 저장하기 전에 응용 프로그램에서 데이터의 유효성을 검사하더라도 FK는 좋은 QA 백업입니다. 첫 번째 근사치의 경우 응용 프로그램에는 항상 데이터 문제가 있습니다. 이와 같은 컨트롤을 시스템 외부에두면 데이터가 자동으로 손상되는 오류 모드 만 초대됩니다.

몇 년 동안 데이터웨어 하우징에서이 작업을 수행하는 것만 큼 좋은 것은 없습니다. 응용 프로그램 코드에서 데이터 무결성을 강화할 수 있다고 생각한 응용 프로그램 개발자가 실수를 저지른 후 조각을 집는 데 시간을 보냅니다. 이 작업을 수행 할 때마다 시간을 투자하면 응용 프로그램 관리 데이터 무결성이 단순한 문제 일 뿐이라는 결론을 내릴 수 있습니다.

또한 쿼리 옵티마이 저는 외래 키를 사용하여 테이블 조인에 대한 정보를 유추 할 수 있으므로 FK는보다 효율적인 쿼리 계획을 제공합니다.

외래 키에도 많은 다른 이점이 있습니다. FK를 데이터베이스에 올려 놓으십시오.


15

참조 무결성은 가장 낮은 수준 (기본 데이터베이스)에서 처리해야합니다. 관계형 데이터베이스 관리 시스템은이를 처리하도록 최적화되어 있습니다. 속담을 재발 명하는 것은 이치에 맞지 않습니다.

DML 문으로 인해 RI 예외가 발생하지 않도록 응용 프로그램 코드에서 도메인 논리를 정의하는 것은 허용되지만 데이터베이스의 외래 키 관계를 대체하는 것으로 간주해서는 안됩니다.


12

DBA에 초점을 맞춘 그룹이기 때문에 여기서는 투표가 중단 될 것으로 예상됩니다.

대부분의 시나리오에서 엄격한 외래 키를 사용하는 것이 최선의 결정이라는 데 동의합니다. 그러나 외래 키가 해결하는 것보다 더 많은 문제를 일으키는 경우가 있습니다.

트래픽이 많은 웹 응용 프로그램과 같이 매우 동시성이 높은 환경을 처리하고 잘 확립 된 강력한 ORM을 사용하는 경우 외래 키로 인해 잠금 문제가 발생하여 서버 확장 및 유지 관리가 어려워 질 수 있습니다. 자식 테이블에서 행을 업데이트 할 때 부모 행도 잠 깁니다. 많은 시나리오에서 경쟁 경합으로 인해 동시성이 크게 제한 될 수 있습니다. 또한 때로는 의도적으로 참조 무결성 규칙을 일시적으로 위반해야하는 보관 프로세스와 같은 개별 테이블에 대해 유지 관리를 수행해야합니다. 외래 키를 사용하면이 작업을 수행하기가 매우 어려울 수 있으며 일부 RDBMS에서 외래 키 제약 조건을 사용하지 않도록 설정하면 상당한 시간이 걸리는 시간이 많이 걸리는 프로세스 인 테이블을 다시 작성하게됩니다.

데이터베이스 외부의 참조 무결성을 이해할 수있는 강력한 프레임 워크를 사용해야한다는 경고를 포함하고 있음을 이해합니다. 그래도 참조 무결성 문제가 발생할 수 있습니다. 그러나 고아 행이나 사소한 참조 무결성 위반이 그다지 중요하지 않은 경우가 많습니다. 웹 응용 프로그램의 대부분이이 범주에 속한다고 주장합니다.

즉, 아무도 페이스 북으로 시작하지 않습니다. 데이터베이스에서 외래 키를 정의하여 시작하십시오. 감시 장치. 문제가 발생하면 이러한 제약 조건 중 일부를 조정하여 삭제해야 할 수도 있습니다.

결론 : 대부분의 데이터베이스에는 외래 키가 있어야합니다. 외래 키가 없으면 높은 동시 환경이 더 나을 수 있습니다. 해당 지점에 도달하면 해당 제약 조건을 삭제하는 것이 좋습니다.

난 지금 난연제를 입을거야.

편집 2012-03-23 ​​7:00 AM

외래 키의 잠금 결과에 대해 생각하면서 내부적으로 내부적으로 생성되어 서버로드에 추가되는 모든 추가 행 조회 비용을 언급하지 않았습니다.

궁극적으로, 내 요점은 외래 키가 자유롭지 않다는 것입니다. 많은 경우에, 그 가치는 가치가 있지만, 그 비용이 그들의 이익을 초과하는 시나리오가 있습니다.

편집 2012-03-23 ​​7:38 AM

구체적으로합시다. 이 예제에서 외래 키 동작을 높이 평가하지는 않지만 MySQL / InnoDB를 선택하고 있지만 가장 친숙하고 가장 일반적으로 사용되는 웹 데이터베이스 일 것입니다. 내가 보여줄 예제와 함께 다른 데이터베이스가 더 나을 것이라고 확신하지는 않습니다.

부모를 참조하는 외래 키가있는 자식 테이블을 고려하십시오. 예를 들어, 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

자식 테이블에 삽입하는 동안 부모 행에서 관련되지 않은 필드를 업데이트 할 수 없습니다. InnoDB가 film_actor의 FK 제약으로 인해 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

users웹 애플리케이션에서 종종 수십 개의 관련 테이블이 있는 테이블을 고려하십시오 . 본질적으로 모든 관련 행에 대한 조작은 상위 행에 대한 갱신을 방지합니다. 외래 키 관계가 여러 개이고 동시성이 많은 경우에는 문제가 될 수 있습니다.

FK 제약 조건으로 인해 테이블 ​​유지 관리에 대한 해결 방법도 까다로울 수 있습니다. : Percona 피터 Zaitsev 더 나은 내가 할 수있는 것보다 그것을 설명하고 이에 대한 블로그 게시물이 하이재킹 이노 디비 외래 키를 .


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Paul White는 GoFundMonica가

6

데이터베이스에서 외래 키를 사용하는 것이 좋습니다. 도움이됩니다

  • 원치 않는 데이터의 가능성을 제거하여 데이터 무결성을 유지
  • 성능을 향상시킵니다. 자동 색인 필드를 사용하는 시스템에서 외래 키 참조는 성능 향상을 제공 할 수 있습니다
  • 프로그래머가 적은 코드를 작성합니다. 처럼ON DELETE CASCADE
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.