คุณควรกำหนดกุญแจต่างประเทศที่ไหน?


คำตอบ:


41

วาง foreign key บนฐานข้อมูล แม้ว่าคุณจะตรวจสอบข้อมูลในแอปพลิเคชันก่อนที่จะบันทึก FK นั้นเป็นข้อมูลสำรอง QA ที่ดี สำหรับการประมาณครั้งแรกแอปพลิเคชันมักมีปัญหาข้อมูล การออกจากการควบคุมเช่นนี้จะเป็นการเชิญโหมดความล้มเหลวที่ข้อมูลเสียหายอย่างเงียบ ๆ

ไม่มีอะไรที่เหมือนกับการทำงานในการจัดเก็บข้อมูลสักสองสามปีเพื่อดูการทำงาน คุณใช้เวลาในการรวบรวมชิ้นส่วนหลังจากความผิดพลาดที่น่าเบื่อโดยนักพัฒนาแอปพลิเคชันที่คิดว่าพวกเขาสามารถบังคับใช้ความถูกต้องของข้อมูลในรหัสแอปพลิเคชัน ใช้เวลาในการทำสิ่งนี้และคุณจะสรุปได้ว่าความสมบูรณ์ของข้อมูลที่จัดการโดยแอปพลิเคชันนั้นน้อยกว่าความคิด

นอกจากนี้เครื่องมือเพิ่มประสิทธิภาพคิวรีสามารถใช้คีย์ต่างประเทศเพื่ออนุมานสิ่งที่เกี่ยวกับการรวมตารางดังนั้น FK จะส่งผลให้มีแผนแบบสอบถามที่มีประสิทธิภาพมากขึ้น

มีประโยชน์อื่น ๆ มากมายสำหรับกุญแจต่างประเทศเช่นกัน ทำสิ่งที่ทุกคนโปรดปราน - นำ FK มาไว้บนฐานข้อมูล


15

Referential Integrity ควรจัดการในระดับต่ำสุดที่เป็นไปได้ซึ่งจะเป็นฐานข้อมูลพื้นฐาน ระบบการจัดการฐานข้อมูลเชิงสัมพันธ์ได้รับการปรับให้เหมาะสมเพื่อจัดการกับสิ่งนี้ มันไม่สมเหตุสมผลเลยที่จะบูรณาการวงล้อที่เป็นสุภาษิต

เป็นที่ยอมรับได้ในการกำหนดตรรกะของโดเมนในรหัสแอปพลิเคชันเพื่อป้องกันคำสั่ง DML ที่จะทำให้เกิดข้อยกเว้น RI แต่ไม่ควรมองว่าเป็นการแทนที่ความสัมพันธ์กับกุญแจต่างประเทศในฐานข้อมูล


12

ฉันจะออกไปบนกิ่งที่นี่อย่างเต็มที่คาดหวังว่าสิ่งนี้จะได้รับการลงคะแนนเพราะนี่คือกลุ่มที่มุ่งเน้น DBA

ฉันยอมรับว่าการใช้คีย์ต่างประเทศที่เข้มงวดเป็นการตัดสินใจที่ดีที่สุดในสถานการณ์ส่วนใหญ่ อย่างไรก็ตามมีบางกรณีที่คีย์ต่างประเทศทำให้เกิดปัญหามากกว่าที่พวกเขาแก้ปัญหา

เมื่อคุณจัดการกับสภาพแวดล้อมที่เกิดขึ้นพร้อมกันอย่างมากเช่นเว็บแอปพลิเคชันที่มีปริมาณการใช้งานสูงและกำลังใช้ ORM ที่ได้รับการยอมรับเป็นอย่างดีกุญแจต่างประเทศอาจทำให้เกิดปัญหาการล็อคที่ทำให้การปรับและบำรุงรักษาเซิร์ฟเวอร์ทำได้ยาก เมื่ออัปเดตแถวในตารางลูกแถวหลักก็จะถูกล็อคเช่นกัน ในหลายสถานการณ์สิ่งนี้สามารถ จำกัด การเกิดขึ้นพร้อมกันอย่างมากเนื่องจากการล็อกการแข่งขัน นอกจากนี้บางครั้งคุณต้องทำการบำรุงรักษาในแต่ละตารางเช่นกระบวนการเก็บถาวรที่คุณอาจต้อง (โดยเจตนา) ทำลายกฎความสมบูรณ์ของ Referential อย่างน้อยก็ชั่วคราว เมื่อใช้คีย์ต่างประเทศสิ่งนี้อาจเป็นเรื่องยากอย่างไม่น่าเชื่อและในบาง RDBMS การปิดใช้งานข้อ จำกัด คีย์ต่างประเทศจะทำให้เกิดการสร้างตารางขึ้นใหม่ซึ่งเป็นกระบวนการที่ใช้เวลานานซึ่งอาจต้องหยุดทำงานเป็นจำนวนมาก

เข้าใจว่าฉันรวมคำเตือนที่คุณต้องใช้กรอบที่แข็งแกร่งที่สามารถเข้าใจ Referential Integrity ภายนอกฐานข้อมูล ถึงกระนั้นคุณก็น่าจะจบลงด้วยปัญหาความสมบูรณ์ของการอ้างอิง อย่างไรก็ตามมีหลายกรณีที่การทำข้อตกลงกับเด็กกำพร้าหรือการละเมิดความสมบูรณ์ของการอ้างอิงเล็กน้อยมีน้อยมาก ฉันขอยืนยันว่าเว็บแอพพลิเคชั่นส่วนใหญ่อยู่ภายใต้หมวดหมู่นี้

ที่ถูกกล่าวว่าไม่มีใครเริ่มเป็น Facebook เริ่มต้นด้วยการกำหนดคีย์ต่างประเทศในฐานข้อมูลของคุณ หน้าจอ หากคุณประสบปัญหาโปรดเข้าใจว่าคุณอาจจำเป็นต้องลดข้อ จำกัด เหล่านั้นลงไป

โดยสรุป: ฐานข้อมูลส่วนใหญ่ควรมีคีย์ต่างประเทศ สภาพแวดล้อมที่เกิดขึ้นพร้อมกันสูงอาจดีกว่าโดยไม่ใช้กุญแจต่างประเทศ หากคุณไปถึงจุดนั้นคุณอาจต้องพิจารณาลดข้อ จำกัด เหล่านั้น

ฉันจะไปสวมชุดทนไฟของฉันตอนนี้

แก้ไข 2012-03-23 ​​7:00 น

ในการคิดถึงผลที่ตามมาของการล็อคกุญแจต่างประเทศฉันละเลยที่จะพูดถึงค่าใช้จ่ายของการค้นหาแถวเพิ่มเติมทั้งหมดที่เกิดขึ้นภายในโดยนัยซึ่งเพิ่มการโหลดของเซิร์ฟเวอร์

ในที่สุดประเด็นของฉันคือกุญแจต่างประเทศนั้นไม่ฟรี ในหลายกรณีค่าใช้จ่ายนั้นคุ้มค่า แต่มีสถานการณ์ที่ค่าใช้จ่ายนั้นเกินผลประโยชน์

แก้ไข 2012-03-23 ​​7:38 AM

มาเป็นรูปธรรมกันเถอะ ฉันเลือก 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 ถึงแถวนั้นจึงไม่สามารถล็อคแบบเอกสิทธิ์ได้ หากคุณย้อนกลับการดำเนินการนั้นและเรียกใช้ 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 สามารถทำให้การแก้ไขปัญหาสำหรับการบำรุงรักษาโต๊ะทำได้ยากเช่นกัน ปีเตอร์ Zaitsev จาก Percona มีบล็อกโพสต์เกี่ยวกับเรื่องนี้ที่อธิบายได้ดีกว่าที่ฉันสามารถ: หักหลัง Innodb คีย์ต่างประเทศ


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
พอลไวท์พูดว่า GoFundMonica

6

เป็นวิธีปฏิบัติที่ดีในการใช้ foreign key ในฐานข้อมูล มันช่วย-

  • เพื่อรักษาความถูกต้องของข้อมูลโดยลบความเป็นไปได้ของข้อมูลที่ไม่ต้องการ
  • เพื่อเพิ่มประสิทธิภาพ ในระบบที่เขตข้อมูลดัชนีอัตโนมัติการอ้างอิงคีย์ต่างประเทศสามารถเพิ่มประสิทธิภาพได้
  • เพื่อเขียนโค้ดให้น้อยลงโดยโปรแกรมเมอร์ ชอบใช้ON DELETE CASCADE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.