MySQL 外鍵約束完整指南:創建、級聯操作、優缺點與最佳實踐
前置條件
- 存儲引擎必須為 InnoDB(MyISAM 不支援外鍵)
- 被引用欄位(父表主鍵/唯一鍵)與引用欄位(子表外鍵)數據類型、長度、字符集、排序規則完全一致
- 被引用欄位必須有索引(主鍵/唯一鍵自動有索引)
- 引用欄位會自動創建索引(如無)
外鍵優點
- 資料庫層面保證引用完整性,防止髒數據
- 支援級聯操作:刪除/更新父記錄時自動處理子記錄
- 文檔化表關係,便於理解數據模型
外鍵缺點(生產環境慎用)
- 性能開銷:INSERT/UPDATE/DELETE 需檢查父表、維護子表索引
- 鎖爭用:外鍵操作會加鎖,高併發下易死鎖、降低吞吐
- 複製/備份複雜:主從複製、邏輯備份需注意順序
- Schema 變更困難:修改父表主鍵、刪除父表需先刪外鍵
- 分庫分表不兼容:跨庫無法建外鍵,微服務架構下通常不適用
創建外鍵語法
CREATE TABLE child_table (
id INT PRIMARY KEY,
parent_id INT,
name VARCHAR(20),
FOREIGN KEY (parent_id) REFERENCES parent_table(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
級聯動作選項
| 選項 | ON DELETE | ON UPDATE | 說明 |
CASCADE | ✓ | ✓ | 級聯刪除/更新子表記錄 |
SET NULL | ✓ | ✓ | 子表外鍵置 NULL(欄位需允許 NULL) |
SET DEFAULT | ✓ | ✓ | 子表外鍵置預設值(需有 DEFAULT) |
RESTRICT | ✓ | ✓ | 拒絕父表操作(有子記錄時) |
NO ACTION | ✓ | ✓ | 標準 SQL,MySQL 中等同 RESTRICT |
實戰示例
-- 父表:部門
CREATE TABLE dept (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50)
) ENGINE=InnoDB;
-- 子表:員工
CREATE TABLE emp (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES dept(dept_id)
ON DELETE SET NULL
ON UPDATE CASCADE
) ENGINE=InnoDB;
-- 測試
INSERT INTO dept VALUES (1, '技術部'), (2, '產品部');
INSERT INTO emp VALUES (101, '張三', 1), (102, '李四', 2);
-- 刪除部門 1 → 員工 101 dept_id 變 NULL
DELETE FROM dept WHERE dept_id = 1;
-- 更新部門 2 → 3 → 員工 102 dept_id 級聯變 3
UPDATE dept SET dept_id = 3 WHERE dept_id = 2;
查看與管理外鍵
-- 查看表外鍵
SHOW CREATE TABLE emp;
-- 刪除外鍵
ALTER TABLE emp DROP FOREIGN KEY fk_name; -- fk_name 由 SHOW CREATE TABLE 獲取
-- 添加外鍵
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(dept_id);
最佳實踐建議
- 核心業務表(訂單、用戶、商品)建議應用層維護完整性,不依賴資料庫外鍵
- 內部管理系統、低併發場景可考慮使用外鍵簡化開發
- 若使用外鍵,首選 ON DELETE RESTRICT/NO ACTION,避免誤級聯刪除大量數據
- 定期檢查
information_schema.TABLE_CONSTRAINTS 監控外鍵狀態
- 壓測驗證外鍵對寫入性能影響,評估是否接受