MySQL 主键详解:作用、使用方法与最佳实践(2025–2026 实用版)
主键(Primary Key)是 MySQL 中最核心、最基础的约束之一,理解它能帮助你设计出高效、可维护的表结构。
一、主键到底是什么?核心作用
主键有三个最本质的作用:
- 唯一性(Uniqueness)
表中每一行数据的主键值必须唯一,不能重复,也不能为 NULL。 - 标识性(Identification)
主键是唯一能确定一行记录的字段(或字段组合),相当于每条数据的“身份证号”。 - 聚集索引(Clustered Index)
在 InnoDB 引擎(MySQL 默认引擎)中,主键就是聚集索引的依据,表数据按照主键顺序物理存储。
一句话总结:
主键 = 唯一不为空 + 决定了数据的物理存储顺序 + 是查找最快的路径
二、主键的几种常见类型对比
| 类型 | 定义方式示例 | 是否允许 NULL | 是否自增 | 存储空间 | 适用场景 | 推荐程度(2025–2026) |
|---|---|---|---|---|---|---|
| 自然主键(业务字段) | order_id VARCHAR(32) | 否 | 否 | 中–大 | 业务有天然唯一标识(如订单号、车牌号) | ★★☆(慎用) |
| 代理主键(自增ID) | id BIGINT AUTO_INCREMENT | 否 | 是 | 8字节 | 绝大多数业务表 | ★★★★★(强烈推荐) |
| UUID 主键 | id CHAR(36) 或 BINARY(16) | 否 | 否 | 36/16字节 | 需要全局唯一、分布式系统 | ★★★☆(特定场景) |
| 复合主键(多列) | PRIMARY KEY (user_id, role_id) | 否 | 否 | — | 多对多关系表、唯一约束场景 | ★★☆(视情况) |
当前主流结论(InnoDB):
- 95%+ 的业务表都应该使用自增 BIGINT 主键(
id BIGINT UNSIGNED AUTO_INCREMENT) - UUID 适合分布式系统,但有性能代价
- 自然主键在大多数场景下不推荐作为主键(虽然可以作为唯一索引)
三、创建主键的几种写法
-- 方式1:建表时直接定义(最常见)
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(50) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 方式2:列级约束
CREATE TABLE orders (
order_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
amount DECIMAL(10,2) NOT NULL
);
-- 方式3:复合主键
CREATE TABLE user_roles (
user_id BIGINT UNSIGNED NOT NULL,
role_id INT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, role_id)
);
-- 方式4:已有表添加主键(慎用)
ALTER TABLE users ADD PRIMARY KEY (id);
四、自增主键最常见的写法(推荐模板)
CREATE TABLE example (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
-- 业务字段
user_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
title VARCHAR(200) NOT NULL DEFAULT '',
status TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0=草稿,1=发布,2=删除',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 索引
INDEX idx_user_id (user_id),
PRIMARY KEY (id)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='示例表';
推荐字段顺序:
主键 → 业务常用查询字段 → 状态/时间字段 → 其他
五、主键相关的常见问题与注意事项
- 为什么不推荐 UUID 做主键?
- 插入性能差(随机性导致页分裂)
- 索引占用空间大(36字节 vs 8字节)
- 排序效率低
- 联合索引时前缀不友好
- 自增 ID 会不会用完?
- BIGINT UNSIGNED:0 ~ 18446744073709551615(约 1840 亿亿)
- 即使每天插入 1 亿条,也要用 5000+ 年
- 主键能改吗?
能,但代价极大(会重建整个表和所有二级索引)
生产环境几乎不要改主键。 - 删除主键后还能恢复吗?
不建议直接删除主键。
如果误删,建议加回同结构主键(但自增会从 1 开始,除非用AUTO_INCREMENT = n指定)。 - 外键一定需要主键吗?
外键引用的一定是被引用表的唯一键(可以是主键,也可以是唯一索引)。
六、实际场景选择建议
| 场景 | 推荐主键类型 | 理由简述 |
|---|---|---|
| 用户表、订单表、文章表 | BIGINT 自增 | 顺序插入、高效、简单 |
| 分布式系统全局唯一ID | UUID v7 / Snowflake / ULID | 避免 ID 冲突 |
| 多对多中间表 | 复合主键(两列) | 天然唯一 + 节省空间 |
| 需要业务含义的编码 | 业务字段 + 唯一索引 | 主键仍用自增,业务码做唯一约束 |
| 日志表、监控表 | BIGINT 自增 或 不设主键 | 追求极致写入性能,可不设主键 |
七、总结口诀
- 默认用:
BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY - 不建议用:VARCHAR、UUID 做主键(除非有特殊需求)
- 必须保证:主键唯一、不为空
- InnoDB 特性:主键 = 聚集索引,决定了数据物理顺序
- 生产建议:主键字段放在表最前面,命名为
id
你现在是在设计新表,还是在优化老表?
或者有具体的场景(比如分布式系统、日志表、多租户等),我可以给你更针对性的表结构建议。