首页 / MySQL 入门教程 / 主键约束

MySQL 入门教程

主键约束

本教程共 46 篇 · 第 31 篇 · 更新于 2026-07-30 · 约 9 分钟阅读

MySQLMySQL 入门教程主键PRIMARY KEY约束AUTO_INCREMENT聚簇索引

31. 主键约束

本节目标:搞清楚主键(Primary Key)是什么、怎么建、怎么选,理解自增主键的工作机制,学会用复合主键应对多列联合唯一场景。

31.1 主键是什么

主键(Primary Key) 是表中用来唯一标识每一行数据的列(或列组合)。它有两个硬性要求:

  1. 唯一:每一行的主键值不能重复;
  2. 非空:主键列不允许为 NULL。

说白了,主键就是一张表的”身份证号”。有了它,你就能精确定位到任意一行数据。

-- 创建带主键的 users 表
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

id INT PRIMARY KEY 这一行做了两件事:把 id 设为主键,同时隐含了 NOT NULL 约束。

Note

在 InnoDB 存储引擎中,主键不只是约束,它还决定了数据的物理存储顺序。InnoDB 会按主键值排序把数据存在 B+ 树的叶子节点上,这就是聚簇索引(Clustered Index)。主键选得好不好,直接影响查询性能。

31.2 单列主键

最常见的主键形式——用一列当主键。上面的例子就是单列主键。

插入数据时,主键值不能重复:

INSERT INTO users(id, username, email) VALUES(1, 'zhangsan', 'zhangsan@test.com');
-- 再插一条 id=1 的会报错
INSERT INTO users(id, username, email) VALUES(1, 'lisi', 'lisi@test.com');
-- ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

主键值也不能为 NULL:

INSERT INTO users(username, email) VALUES('wangwu', 'wangwu@test.com');
-- 如果 id 没有默认值,会报错

31.3 自增主键

手动指定主键值太麻烦,尤其是数据量大的时候。MySQL 提供了 AUTO_INCREMENT 属性,让主键自动增长。

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入数据时不指定 id,MySQL 自动分配:

INSERT INTO users(username, email, age) VALUES('zhangsan', 'zhangsan@test.com', 25);
INSERT INTO users(username, email, age) VALUES('lisi', 'lisi@test.com', 30);

SELECT id, username FROM users;
-- 结果:
-- 1 | zhangsan
-- 2 | lisi

自增值的几个特点

  • 自增值从 1 开始(默认),每次加 1;
  • 如果删除了某行,那个自增值不会回收(InnoDB 引擎下);
  • 可以用 AUTO_INCREMENT = N 指定起始值。
-- 让自增从 1000 开始
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50)
) AUTO_INCREMENT = 1000;
-- 或者修改已有表的自增起始值
ALTER TABLE users AUTO_INCREMENT = 1000;
Tip

实际项目中,自增主键的起始值设大一点(比如 10000),留出前面几百个号给特殊数据用,是个常见的做法。

查看当前自增值

SHOW TABLE STATUS LIKE 'users'\G
-- Auto_increment 字段就是下一个自增值

也可以查 information_schema

SELECT AUTO_INCREMENT FROM information_schema.TABLES
WHERE TABLE_SCHEMA = '你的库名' AND TABLE_NAME = 'users';
Warning

InnoDB 的自增值在 8.0 之前是持久化到内存里的,重启后可能重置为 MAX(id)+1。MySQL 8.0 起改为持久化到 redo log,重启后自增值不会回退。本教程基线版本 26.7.0 已经是持久化机制,不用担心这个问题。

31.4 复合主键

有时候一列没法唯一标识一行,需要多列组合当主键,这就是复合主键(Composite Primary Key)。

比如订单明细表,一个订单有多件商品,用 order_id + product_id 组合才能唯一标识一行:

CREATE TABLE order_items (
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL DEFAULT 1,
    price DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (order_id, product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

复合主键要求:组合值唯一,但单独某一列可以重复。

INSERT INTO order_items(order_id, product_id, quantity, price) VALUES(1, 101, 2, 9.90);
INSERT INTO order_items(order_id, product_id, quantity, price) VALUES(1, 102, 1, 19.90);  -- order_id 重复,但组合不重复,OK
INSERT INTO order_items(order_id, product_id, quantity, price) VALUES(1, 101, 3, 9.90);
-- ERROR 1062: Duplicate entry '1-101' for key 'PRIMARY'
Note

复合主键的列顺序很重要。(order_id, product_id)(product_id, order_id) 在约束效果上是一样的(都要求组合唯一),但在 InnoDB 聚簇索引的排序和查询效率上有区别。把区分度高的列放前面通常更好。

31.5 建表后添加和删除主键

表已经建好了,后面想加主键也行:

-- 前提:要设为主键的列不能有 NULL 值和重复值
ALTER TABLE users ADD PRIMARY KEY (id);

删除主键:

ALTER TABLE users DROP PRIMARY KEY;
Warning

如果主键列上有 AUTO_INCREMENT 属性,必须先去掉自增属性才能删除主键,否则会报错。实际开发中,主键一旦定好就别轻易改。

31.6 主键选取原则

主键不是随便选的,选错了后面性能和维护都受影响。几条经验法则:

1. 优先用自增整数列

InnoDB 的聚簇索引按主键排序存储数据。自增主键每次插入的值都是递增的,新数据直接追加到 B+ 树末尾,页分裂少,写入效率高。

如果用 UUID 这类无序字符串当主键,每次插入位置随机,会导致频繁的页分裂和数据移动,性能明显下降。

主键类型插入性能存储空间排序连续性
自增 INT4 字节连续
自增 BIGINT8 字节连续
UUID16 字节随机
业务字段(如手机号)不固定不连续

2. 主键要稳定不变

主键一旦确定就不应该频繁修改。InnoDB 中改主键值意味着数据要在 B+ 树里搬家,代价很大。别用可能变动的业务字段(如手机号、邮箱)当主键。

3. 主键尽量短

InnoDB 的所有二级索引都存储了主键值。主键越长,每个二级索引节点能放的数据越少,索引树越高,查询越慢。INT(4 字节)或 BIGINT(8 字节)是最佳选择。

4. 不要用业务字段当主键

业务规则是会变的。比如手机号可能升级、身份证号可能输错需要改。用自增 ID 做主键,业务字段做唯一索引,是更安全的方案。

Tip

我之前踩过这个坑——早期项目用邮箱当主键,后来用户要改邮箱,连带所有关联表的外键都要改,改到崩溃。从那以后我一律用自增 ID 做主键,业务唯一性用 UNIQUE 约束保证。

31.7 小结

  • 主键保证唯一和非空,是定位数据的”身份证”;
  • AUTO_INCREMENT 让主键自动增长,InnoDB 8.0+ 持久化自增值;
  • 复合主键用多列组合,组合值唯一即可,单列可重复;
  • 主键选取原则:自增整数、稳定不变、尽量短、不用业务字段;
  • InnoDB 中主键即聚簇索引,直接影响数据存储和查询性能。

下一节讲外键约束,看两张表怎么通过主键关联起来。