Де слід визначити іноземні ключі?


Відповіді:


41

Покладіть іноземні ключі в базу даних. Навіть якщо ви перевіряєте дані в програмі, перш ніж зберегти їх, FK - це гарне копіювання QA. Для першого наближення програми завжди мають проблеми з даними. Залишаючи такі керування поза системою, просто запрошуються режими відмови, коли дані мовчки пошкоджуються.

Немає нічого подібного до роботи в сховищі даних протягом декількох років, щоб побачити це в дії. Ви витрачаєте свій час на збирання фрагментів після поважних помилок розробників додатків, які вважали, що вони можуть забезпечити цілісність даних у коді програми. Витратьте будь-який час, зробивши це, і ви зробите висновок, що цілісність даних, керованих додатками, - це трохи більше, ніж прихованість.

Крім того, оптимізатор запитів може використовувати зовнішні ключі, щоб зробити висновки про приєднання до таблиці, тому ФК призведе до більш ефективних планів запитів.

Існує безліч інших переваг і для іноземних ключів. Зроби всі прихильність - помістіть ФК у базу даних.


15

Референтна доброчесність повинна оброблятися на найнижчому можливому рівні, який був би базовою базою даних. Системи управління реляційними базами даних оптимізовані для цього. Немає сенсу винаходити поговорне колесо.

Доцільно визначити логіку домену в коді програми, щоб запобігти оператору DML навіть не викликати виключення RI, але це не повинно розглядатися як заміна зовнішніх ключових зв'язків у базі даних.


12

Я збираюся вийти на кінцівку тут, повністю очікуючи, що це буде озвучено, оскільки це група, орієнтована на DBA.

Я погоджуюся, що використання строгих зовнішніх ключів - найкраще рішення у більшості сценаріїв. Однак є випадки, коли сторонні ключі викликають більше проблем, ніж вони вирішують.

Коли ви маєте справу з дуже одночасним середовищем, таким як веб-додаток з високим трафіком, і використовуєте чітко встановлений надійний ORM, сторонні ключі можуть спричинити проблеми з блокуванням, які ускладнюють масштабування та підтримку сервера. Під час оновлення рядків у дочірній таблиці батьківський рядок також блокується. У багатьох сценаріях це може різко обмежити паралельність через блокування суперечок. Крім того, іноді доводиться виконувати технічне обслуговування окремих таблиць, таких як архівні процеси, де вам, можливо, доведеться (навмисно) порушувати референтні правила цілісності хоча б тимчасово. Якщо встановлені зовнішні ключі, це може бути надзвичайно важким, а в деяких RDBMS відключення обмежень зовнішніх ключів призведе до відновлення таблиці, що займає багато часу, який може вимагати значного простою.

Зрозумійте, що я включаю застереження про те, що ви повинні використовувати надійний фреймворк, здатний розуміти референтну цілісність зовнішньої бази даних. Тим не менш, у вас, швидше за все, виникнуть деякі референтні проблеми цілісності. Однак, є багато випадків, коли просто не так вже й важливо мати осиротілі ряди або незначні порушення референтної цілісності. Я заперечую, що більшість веб-додатків підпадають під цю категорію.

Це, як кажуть, ніхто не починає, як Facebook. Почніть з визначення іноземних ключів у вашій базі даних. Монітор. Якщо у вас виникли проблеми, зрозумійте, що вам може знадобитися скинути деякі з цих обмежень для масштабування.

На закінчення: Більшість баз даних повинні мати зовнішні ключі. Сильно одночасне середовище може бути краще без сторонніх ключів. Якщо ви досягнете цієї точки, можливо, вам доведеться розглянути можливість скасування цих обмежень.

Я зараз піду надіти свій вогнезахисний костюм.

EDIT 2012-03-23 ​​7:00

Розмірковуючи про наслідки блокування сторонніх ключів, я знехтував згадати вартість усіх додаткових пошукових рядків, які неявно генеруються внутрішньо, додаючи до завантаження сервера.

Зрештою, моя думка полягає в тому, що іноземні ключі не є безкоштовними. У багатьох випадках вартість того варта, але є сценарії, коли ці витрати перевищують їхню користь.

EDIT 2012-03-23 ​​7:38

Будьмо конкретні. У цьому прикладі я вибираю MySQL / InnoDB, який не дуже поважають за свою зовнішньополітичну поведінку, але це те, що мені найбільше знайоме і, ймовірно, є найбільш часто використовуваною веб-базою даних. Я не впевнений, що інша база даних стане кращою для прикладу, який я збираюся показати.

Розгляньте дочірню таблицю із зовнішнім ключем, що посилається на батьків. Як приклад, див таблиці таблиць фільму та film_actor у базі даних зразка sakila в 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

Відповідним обмеженням для мого прикладу є 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.film_id = 508 через обмеження FK на film_actor, таким чином ОНОВЛЕННЯ до цього рядка не може отримати необхідний ексклюзивний замок. Якщо ви скасуєте цю операцію та спершу запустите UPDATE, у вас буде така ж поведінка, але ВСТАВКА заблокована.

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таблицю у веб-додатку, де часто є десятки пов’язаних таблиць. По суті, будь-яка операція на будь-якому пов'язаному рядку перешкоджає оновленню батьківського рядка. Це може бути складною проблемою, коли у вас є багато іноземних ключових відносин і багато одночасності.

Обмеження ФК також можуть скласти труднощі для обслуговування сервісного столу. У Петра Зайцева з Percona є повідомлення в блозі про це, що пояснює це краще, ніж я можу: Викрадення закордонних ключів Innodb .


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Пол Білий каже, що GoFundMonica

6

Добре застосовувати іноземний ключ у базі даних. Це допомагає-

  • зберегти цілісність даних, усуваючи можливість небажаних даних
  • для підвищення продуктивності. У системах, які містять поля автоматичного індексування, іноземні ключові посилання можуть підвищити продуктивність
  • писати менше коду програмістом. як, використовуючиON DELETE CASCADE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.