Bạn nên xác định khóa ngoại ở đâu?


Câu trả lời:


41

Đặt các khóa ngoại trên cơ sở dữ liệu. Ngay cả khi bạn xác thực dữ liệu trong ứng dụng trước khi bạn lưu nó, FK vẫn là một bản sao lưu QA tốt. Đối với một xấp xỉ đầu tiên, các ứng dụng luôn có vấn đề về dữ liệu. Rời khỏi các điều khiển như thế này ra khỏi hệ thống chỉ mời các chế độ thất bại trong đó dữ liệu bị hỏng một cách im lặng.

Không có gì giống như làm việc trong kho dữ liệu trong một vài năm để thấy điều này hoạt động. Bạn dành thời gian để nhặt các mảnh sau những lỗi sai lầm của các nhà phát triển ứng dụng, những người nghĩ rằng họ có thể thực thi tính toàn vẹn dữ liệu trong mã ứng dụng. Hãy dành bất cứ lúc nào để làm điều này và bạn sẽ kết luận rằng tính toàn vẹn dữ liệu được quản lý ứng dụng ít hơn một sự tự phụ.

Ngoài ra, trình tối ưu hóa truy vấn có thể sử dụng các khóa ngoại để suy luận mọi thứ về các phép nối bảng, do đó FK sẽ dẫn đến các kế hoạch truy vấn hiệu quả hơn.

Có rất nhiều lợi ích khác cho khóa nước ngoài là tốt. Làm mọi người ủng hộ - đưa FK vào cơ sở dữ liệu.


15

Tính toàn vẹn tham chiếu nên được xử lý ở mức thấp nhất có thể, đó sẽ là cơ sở dữ liệu cơ bản. Hệ thống quản lý cơ sở dữ liệu quan hệ được tối ưu hóa để xử lý việc này. Nó không có ý nghĩa để phát minh lại bánh xe câu tục ngữ.

Có thể chấp nhận định nghĩa logic miền trong mã ứng dụng để ngăn câu lệnh DML thậm chí gây ra ngoại lệ RI, nhưng điều này không nên được xem như là sự thay thế cho các mối quan hệ khóa ngoài trong cơ sở dữ liệu.


12

Tôi sẽ đi ra ngoài ở một chi ở đây hoàn toàn mong đợi điều này sẽ được bình chọn vì đây là nhóm tập trung vào DBA.

Tôi đồng ý rằng sử dụng khóa ngoại nghiêm ngặt là quyết định tốt nhất trong hầu hết các kịch bản. Tuy nhiên, có một số trường hợp khóa ngoại gây ra nhiều vấn đề hơn giải quyết.

Khi bạn đang xử lý môi trường đồng thời rất cao như ứng dụng web có lưu lượng truy cập cao và đang sử dụng ORM mạnh mẽ, được thiết lập tốt, các khóa ngoại có thể gây ra sự cố khóa khiến việc mở rộng và bảo trì máy chủ trở nên khó khăn. Khi cập nhật các hàng trong bảng con, hàng cha cũng bị khóa. Trong nhiều kịch bản, điều này có thể hạn chế đáng kể sự đồng thời do sự tranh chấp khóa. Ngoài ra, đôi khi bạn phải thực hiện bảo trì trên các bảng riêng lẻ, chẳng hạn như các quy trình lưu trữ mà bạn có thể cần (cố ý) phá vỡ các quy tắc toàn vẹn tham chiếu, ít nhất là tạm thời. Với các khóa ngoại tại chỗ, điều này có thể cực kỳ khó khăn và trong một số RDBMS, việc vô hiệu hóa các ràng buộc khóa ngoại sẽ gây ra việc xây dựng lại bảng, một quá trình tốn thời gian có thể cần thời gian chết đáng kể.

Hiểu rằng tôi bao gồm sự cảnh báo rằng bạn phải sử dụng một khung mạnh mẽ có khả năng hiểu tính toàn vẹn tham chiếu bên ngoài cơ sở dữ liệu. Tuy nhiên, bạn có thể sẽ kết thúc với một số vấn đề toàn vẹn tham chiếu. Tuy nhiên, có nhiều trường hợp không phải là vấn đề lớn khi có hàng mồ côi hoặc vi phạm tính toàn vẹn tham chiếu nhỏ. Tôi sẽ tranh luận rằng phần lớn các ứng dụng web thuộc thể loại này.

Điều đó đang được nói, không ai bắt đầu như Facebook. Bắt đầu bằng cách xác định khóa ngoại trong cơ sở dữ liệu của bạn. Giám sát. Nếu bạn kết thúc có vấn đề, hãy hiểu rằng bạn có thể cần phải bỏ một số ràng buộc đó để mở rộng quy mô.

Tóm lại: Hầu hết các cơ sở dữ liệu nên có khóa ngoại. Môi trường đồng thời cao có thể tốt hơn mà không cần khóa ngoại. Nếu bạn đạt đến điểm đó, bạn có thể cần phải xem xét loại bỏ những ràng buộc đó.

Bây giờ tôi sẽ đi tặng bộ đồ chống cháy của mình.

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

Khi nghĩ về hậu quả khóa của khóa ngoại, tôi đã bỏ qua việc đề cập đến chi phí của tất cả các tra cứu hàng bổ sung được tạo ngầm trong nội bộ, thêm vào tải máy chủ.

Cuối cùng, quan điểm của tôi là khóa ngoại không miễn phí. Trong nhiều trường hợp, chi phí là xứng đáng, nhưng có những kịch bản mà chi phí đó vượt quá lợi ích của họ.

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

Hãy cụ thể. Tôi đang chọn MySQL / InnoDB trong ví dụ này, không được tôn trọng cao vì hành vi khóa ngoại của nó, nhưng đó là thứ tôi quen thuộc nhất và có khả năng là cơ sở dữ liệu web được sử dụng phổ biến nhất. Tôi không chắc chắn cơ sở dữ liệu khác sẽ tốt hơn với ví dụ tôi sắp trình bày.

Xem xét một bảng con có khóa ngoại tham chiếu đến cha mẹ. Ví dụ, xem bảng phim và film_actor trong cơ sở dữ liệu mẫu sakila trong 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

Ràng buộc có liên quan là film_actor (fk_film_actor_film) cho ví dụ của tôi.

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

Lưu ý rằng tôi không thể cập nhật trường không liên quan trong hàng cha trong khi chèn vào bảng con. Điều này xảy ra do InnoDB đang giữ một khóa chia sẻ trên hàng trong đó film.film_id = 508 do ràng buộc FK trên film_actor, do đó, CẬP NHẬT cho hàng đó không thể có khóa độc quyền. Nếu bạn đảo ngược thao tác đó và chạy CẬP NHẬT trước, bạn có hành vi tương tự, nhưng INSERT bị chặn.

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

Hãy xem xét một usersbảng trong một ứng dụng web, nơi thường có hàng tá các bảng liên quan. Về cơ bản, bất kỳ hoạt động nào trên bất kỳ hàng liên quan nào đều ngăn chặn cập nhật cho hàng cha. Đó có thể là một vấn đề đầy thách thức khi bạn có nhiều mối quan hệ khóa ngoại và nhiều sự đồng thời.

Các ràng buộc FK cũng có thể làm cho các cách giải quyết cho việc bảo trì bảng cũng khó khăn. Peter Zaitsev từ Percona có một bài đăng trên blog về điều này giải thích điều đó tốt hơn tôi có thể: Cướp bóc khóa ngoại khóa Innodb .


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White nói GoFundMonica

6

Đó là một thực hành tốt để sử dụng khóa ngoại trong cơ sở dữ liệu. Nó giúp-

  • để giữ tính toàn vẹn dữ liệu bằng cách loại bỏ khả năng dữ liệu không mong muốn
  • để tăng hiệu suất. Trong các hệ thống có trường chỉ mục tự động, tham chiếu khóa ngoài có thể tăng hiệu suất
  • để viết ít mã hơn bởi các lập trình viên. thích, sử dụngON DELETE CASCADE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.