MySQL【表的约束下】

【MySQL 笔记】表的约束(下)—— 外键详解 + 级联行为 + 约束 vs 索引 + 生产常见坑

上一期我们把基础约束(NOT NULL、DEFAULT、UNIQUE、PRIMARY KEY、CHECK、AUTO_INCREMENT)过了一遍,今天重点把最复杂、也最有争议的外键约束(FOREIGN KEY)讲透,顺带对比约束与索引的区别,以及生产环境中大家最容易踩的坑。

一、外键约束核心特性速查

特性说明是否强制性能影响生产推荐度(2026视角)
参照完整性从表字段值必须在主表对应字段中存在(或为NULL)中等
允许NULL外键字段通常允许NULL(表示“无关联”)常见
级联操作主表删除/更新时,从表可自动跟随(CASCADE、SET NULL 等)可选有影响谨慎使用
自动建索引InnoDB 会在外键字段上自动创建普通索引(如果没有合适索引)有利
约束名可自定义名称,便于后期删除/修改推荐强烈建议
跨库/跨引擎外键必须在同一数据库,且两表都用 InnoDB

二、外键定义的两种写法

  1. 列级(简单,但无法指定级联行为)
user_id BIGINT,
FOREIGN KEY (user_id) REFERENCES users(id)
  1. 表级(推荐!可起名、可定义级联)
CREATE TABLE orders (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no    VARCHAR(32) UNIQUE NOT NULL,
    user_id     BIGINT NOT NULL,
    amount      DECIMAL(10,2) NOT NULL,
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT fk_orders_user 
        FOREIGN KEY (user_id) 
        REFERENCES users(id)
        ON DELETE RESTRICT          -- 默认行为
        ON UPDATE CASCADE
) ENGINE=InnoDB;

三、级联行为详解(ON DELETE / ON UPDATE)

选项DELETE 时行为UPDATE 时行为适用场景风险/注意事项
RESTRICT / NO ACTION禁止删除(默认)禁止修改主表主键保护数据完整性最安全,但业务删除会报错
CASCADE主删从也删主改从跟着改彻底清理关联数据(如删除用户删订单)误删风险高,级联可能很多行
SET NULL主删 → 从表外键字段设为 NULL主改 → 从表外键设为 NULL“可选关联”场景从表字段必须允许 NULL
SET DEFAULT主删 → 从表外键设为 DEFAULT 值主改 → 从表设为 DEFAULT 值极少用DEFAULT 必须合理

最常见组合推荐(生产中):

-- 强保护型(推荐大多数业务)
ON DELETE RESTRICT ON UPDATE CASCADE

-- 自动清理型(用户相关数据)
ON DELETE CASCADE ON UPDATE CASCADE

-- 可选关联型(比如优惠券使用记录)
ON DELETE SET NULL ON UPDATE CASCADE

四、约束 vs 索引 对比(面试常考)

项目约束(Constraint)索引(Index)谁更重?备注
目的保证数据正确性、一致性加速查询约束 > 索引约束是业务规则,索引是性能优化
唯一约束 UNIQUE强制唯一 + 自动建唯一索引只是加速 + 允许重复(普通索引)UNIQUE 约束 = 唯一索引 + 唯一性校验
外键强制参照完整性 + 自动建普通索引单独建索引不强制参照完整性约束更强外键依赖索引,但索引不等于外键
删除约束ALTER TABLE DROP FOREIGN KEY …ALTER TABLE DROP INDEX …约束名 vs 索引名
性能开销插入/更新/删除时校验(外键最明显)插入/更新时维护索引约束稍大大表外键慎用
可否禁用SET FOREIGN_KEY_CHECKS=0(临时关闭)不可直接禁用用于批量导入数据

一句话总结
约束 = 业务规则 + 附带索引
索引 = 纯性能工具

五、生产中最容易踩的 10 个外键/约束坑(真实血泪)

  1. 忘记 ENGINE=InnoDB(MyISAM 不支持外键)
  2. 主表字段不是 PRIMARY KEY 或 UNIQUE(外键参照必须是主键或唯一键)
  3. 字段类型不一致(int vs bigint、unsigned vs signed)
  4. 大表加外键导致锁表时间过长(尤其是已有大量数据时)
  5. 误用 CASCADE 导致连锁删除(删一个用户把几十万订单全删了)
  6. 批量导入数据时不关外键检查(超慢或报错)
   SET FOREIGN_KEY_CHECKS=0;
   -- 导入...
   SET FOREIGN_KEY_CHECKS=1;
  1. 忘记给外键字段建索引(虽然自动建,但有时名字对不上)
  2. 约束名重复或没起名,导致后期 DROP 约束很麻烦
  3. 跨库外键(不可能,MySQL 不支持)
  4. 8.0 前用 CHECK 约束(只是摆设,不生效)

六、推荐的现代建表约束模板(2026 年风格)

CREATE TABLE order_items (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    order_id        BIGINT UNSIGNED NOT NULL,
    product_id      BIGINT UNSIGNED NOT NULL,
    quantity        INT UNSIGNED NOT NULL DEFAULT 1,
    unit_price      DECIMAL(10,2) NOT NULL,
    created_at      DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    updated_at      DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),

    CONSTRAINT fk_order_items_order 
        FOREIGN KEY (order_id) 
        REFERENCES orders(id)
        ON DELETE CASCADE
        ON UPDATE CASCADE,

    CONSTRAINT fk_order_items_product 
        FOREIGN KEY (product_id) 
        REFERENCES products(id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE,

    INDEX idx_order_id (order_id),
    INDEX idx_product_id (product_id)
) ENGINE=InnoDB 
  DEFAULT CHARSET=utf8mb4 
  COLLATE=utf8mb4_unicode_ci 
  COMMENT='订单明细表';

下一期预告建议:MySQL 索引全家桶(上)—— 索引类型 + 创建原则 + 复合索引最左前缀

有外键级联写法、禁用外键场景、或具体业务表结构想讨论的,欢迎留言~
祝大家约束加得合理,数据一致性拉满!🔒

文章已创建 5186

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部