參考完整性:確保子表外鍵值必須存在於父表主鍵/唯一鍵中,防止孤兒資料。
CREATE TABLE city (
city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
city VARCHAR(50) NOT NULL,
country_id SMALLINT UNSIGNED NOT NULL,
KEY idx_fk_country_id (country_id), -- 建議先建索引
CONSTRAINT fk_city_country
FOREIGN KEY (country_id)
REFERENCES country (country_id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
PRIMARY KEY (city_id)
) ENGINE=InnoDB;
-- 步驟 1:建立索引 (外鍵欄位必須有索引)
ALTER TABLE city ADD INDEX idx_city_country (country_id);
-- 步驟 2:加外鍵約束 (注意用 ADD 而非 MODIFY)
ALTER TABLE city
ADD CONSTRAINT fk_city_country
FOREIGN KEY (country_id)
REFERENCES country (country_id)
ON DELETE CASCADE;
-- 必須知道約束名稱
ALTER TABLE city DROP FOREIGN KEY fk_city_country;
-- 若不知名稱,先查看
SHOW CREATE TABLE city\G
-- 輸出中找到 CONSTRAINT `fk_xxx` FOREIGN KEY ...
| 動作 | 父表操作 | 子表自動響應 | 適用場景 |
|---|---|---|---|
RESTRICT (預設) | 刪除/更新 | 拒絕操作,報錯 | 嚴格保護,不允許級聯 |
CASCADE | 刪除/更新 | 同步刪除/更新子表匹配行 | 強關聯、生命週期綁定 (訂單-明細) |
SET NULL | 刪除/更新 | 子表外鍵欄位設為 NULL | 弱關聯、子表獨立存在 (外鍵欄位必須允許 NULL) |
NO ACTION | 刪除/更新 | 同 RESTRICT (標準 SQL 語法) | 相容性考量 |
SET DEFAULT | 刪除/更新 | 子表外鍵設為預設值 | 極少用,MySQL 實際不支援 |
ON DELETE RESTRICT 和 ON DELETE CASCADE **不能同時指定**ON UPDATE CASCADE)| 影響面 | 說明 | 緩解策略 |
|---|---|---|
| 鎖擴大 | 父表 UPDATE/DELETE 需鎖定子表匹配行檢查 | 子表 FK 欄位**必須有索引**,否則全表掃描鎖表 |
| 死鎖風險 | 並發父子表操作易產生死鎖 | 固定存取順序、縮短事務、避免長事務 |
| 寫入延遲 | 每次 INSERT 子表需查詢父表驗證 | 熱點父表考慮應用層快取驗證 |
| DDL 阻塞 | ALTER TABLE 親表需等待子表釋放鎖 | 維護窗口操作、pt-online-schema-change |
-- 關閉 (匯入資料、大批量操作、重建表)
SET FOREIGN_KEY_CHECKS = 0;
-- 執行操作...
LOAD DATA INFILE ...;
TRUNCATE TABLE ...;
-- 開啟 (務必恢復!)
SET FOREIGN_KEY_CHECKS = 1;
適用場景: 多表資料匯入順序不確定、LOAD DATA、ALTER TABLE 重建表、從備份恢復。
fk_子表_父表,便於管理與刪除RESTRICT + 應用層軟刪除Innodb_row_lock_waits、Innodb_deadlocks、外鍵檢查耗時| 維度 | 資料庫外鍵 | 應用層校驗 |
|---|---|---|
| 強一致性 | ✅ ACID 保證 | ⚠️ 依賴事務/分散式鎖 |
| 效能 | 寫入額外開銷 | 可快取、批次、異步 |
| 靈活性 | 低 (DDL 困難) | 高 (業務邏輯可變) |
| 遷移/重構 | 困難 | 容易 |
| 推薦場景 | 核心主從表、資料量中小、嚴格一致性要求 | 高併發、大表、微服務、最終一致性可接受 |
外鍵是 **資料完整性的守門員**,用得好是護身符,用不好是效能殺手。**中小型核心業務表建議啟用,大表高併發場景建議應用層接管**。無論選擇哪種,**索引、監控、死鎖預案** 都是必修課。