【MySQL 笔记】约束(Constraints)完整详解(2026 年生产视角)
约束是数据库在数据层面强制执行规则的核心机制,它能极大提高数据质量、减少脏数据、降低业务代码的校验负担。
MySQL(InnoDB 引擎)支持的约束类型如下,按重要程度和使用频率排序:
一、MySQL 主要约束类型对比表
| 约束类型 | 关键字 | 强制规则 | 是否允许 NULL | 是否允许重复 | 每表数量限制 | 是否自动创建索引 | 级别 | 生产使用频率(2026) |
|---|---|---|---|---|---|---|---|---|
| 非空约束 | NOT NULL | 值不能为空 | 否 | 是 | 多 | 否 | 列级 | ★★★★★ |
| 默认值约束 | DEFAULT | 未提供值时自动填充 | — | — | 多 | 否 | 列级 | ★★★★☆ |
| 唯一约束 | UNIQUE | 值全局唯一(NULL 除外) | 是(多 NULL) | 否 | 多 | 是(唯一索引) | 列级/表级 | ★★★★★ |
| 主键约束 | PRIMARY KEY | 唯一 + 非空 + 表唯一标识 | 否 | 否 | 1 | 是(聚集索引) | 列级/表级 | ★★★★★(必有) |
| 自增属性 | AUTO_INCREMENT | 自动递增(通常配合主键) | — | — | 1(每表) | — | 列级 | ★★★★★ |
| 外键约束 | FOREIGN KEY | 参照完整性(必须存在于主表) | 是(视情况) | — | 多 | 是(普通索引) | 表级 | ★★★★☆(谨慎) |
| 检查约束 | CHECK | 自定义条件表达式 | — | — | 多 | 否 | 列级/表级 | ★★★★☆(8.0.16+) |
二、各种约束写法对比(列级 vs 表级)
CREATE TABLE users (
-- 列级约束(简单、常见)
id BIGINT UNSIGNED AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(120) UNIQUE,
phone VARCHAR(20) DEFAULT '未设置',
age TINYINT UNSIGNED CHECK (age >= 0 AND age <= 150),
status TINYINT UNSIGNED DEFAULT 1 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),
-- 表级约束(可以起名字、支持复合约束)
PRIMARY KEY (id), -- 表级主键
CONSTRAINT uk_users_email UNIQUE (email), -- 表级唯一
CONSTRAINT uk_users_phone_region UNIQUE (phone, country_code), -- 复合唯一
CONSTRAINT chk_users_age CHECK (age >= 18 OR is_minor = 1),
CONSTRAINT chk_users_status CHECK (status IN (0,1,2,9))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
三、约束行为与注意事项速查
| 约束类型 | 插入/更新 NULL 时 | 插入重复值时 | 删除主表记录时(有外键) | 修改主表主键时 | 性能开销排序 | 生产推荐写法 |
|---|---|---|---|---|---|---|
| NOT NULL | 报错 | 允许 | — | — | 极低 | 几乎所有重要字段都加 |
| UNIQUE | 允许(多 NULL) | 报错 | — | — | 中等 | 手机号、邮箱、订单号 |
| PRIMARY KEY | 报错 | 报错 | — | — | 中等(聚集索引) | 每张表必须有 |
| AUTO_INCREMENT | — | — | — | — | 低 | BIGINT UNSIGNED + PRIMARY KEY |
| FOREIGN KEY | 允许(若允许 NULL) | — | 取决于 ON DELETE | 取决于 ON UPDATE | 中~高 | 小中型项目谨慎,大表慎用 |
| CHECK | 按条件 | 按条件 | — | — | 低~中 | 8.0.16+ 推荐用(状态、范围) |
四、最常见的生产约束组合模板(2026 年推荐)
CREATE TABLE orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '订单主键',
order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单编号',
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
total_amount DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '订单总金额',
status TINYINT UNSIGNED NOT NULL DEFAULT 0
CHECK (status IN (0,1,2,3,4,5,9)) COMMENT '订单状态:0-待支付...',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
-- 外键(视项目规模可选)
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE RESTRICT -- 防止误删用户导致订单孤立
ON UPDATE CASCADE,
INDEX idx_user_id (user_id),
INDEX idx_status_created (status, created_at)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='订单主表';
五、生产中最容易踩的约束坑(血泪经验)
- 忘记 NOT NULL → 出现大量空值,业务判断爆炸
- 用 INT 做主键/用户ID → 超过 42 亿就溢出(用 BIGINT UNSIGNED)
- 外键加在高并发大表上 → 插入/删除性能急剧下降,甚至死锁
- 没给外键字段建索引 → InnoDB 会自动建,但名字不一致时维护麻烦
- CHECK 约束写在 5.7 或 8.0.15 以下 → 只是语法糖,不生效
- 主键不是自增 → 分布式 ID 场景除外,否则容易出现主键冲突
- 所有字段都加 DEFAULT NULL → 违背 NOT NULL 的初衷
- 忘记写 COMMENT → 半年后没人敢改表
六、约束管理常用命令(开发/运维必备)
-- 查看所有约束
SHOW CREATE TABLE orders\G
-- 查看外键信息
SELECT * FROM information_schema.table_constraints
WHERE table_name = 'orders' AND constraint_type = 'FOREIGN KEY';
-- 删除外键(必须知道约束名)
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user;
-- 删除主键(极少用,大表慎重)
ALTER TABLE old_table DROP PRIMARY KEY;
-- 添加唯一约束(后期加)
ALTER TABLE users ADD CONSTRAINT uk_users_email UNIQUE (email);
下一期预告建议:MySQL 索引全家桶(上)—— 聚簇索引 vs 非聚簇索引 + 最左前缀 + 覆盖索引 + 索引失效场景
你目前项目里约束用得最多的类型是哪些?
有没有遇到过外键性能问题、CHECK 不生效、或者唯一约束冲突的真实案例?
欢迎留言,我们可以针对性继续深入~
祝大家建表加约束一步到位,数据干净又安全!🛡️