
主键索引:一种特殊的唯一索引,每张表只能有一个,用于唯一标识每一行记录,InnoDB 中主键是 聚簇索引。
唯一索引:保证索引列的值唯一,一张表可以有多个唯一索引,InnoDB 中唯一索引是 二级索引(非聚簇索引)。
核心区别对比:
对比维度 | 主键索引(PRIMARY) | 唯一索引(UNIQUE) |
|---|---|---|
数量限制 | 每表 只能有 1 个 | 每表 可以有多个 |
NULL 值 | 不允许 NULL | 允许 NULL(但最多 1 个) |
存储结构 | 聚簇索引,叶子节点存完整数据 | 二级索引,叶子节点存主键值 |
索引回表 | 不需要回表 | 需要回表(查询非索引列时) |
创建语法 | PRIMARY KEY (col) | UNIQUE KEY uk_name (col) |
是否必须 | 建议有,但不强制 | 按需创建 |
一句话总结:主键是 唯一的聚簇索引,不允许 NULL;唯一索引是 可以有多个的非聚簇索引,允许 NULL。
主键索引和唯一索引在 InnoDB 中的存储结构完全不同:

img
上图对比了主键索引和唯一索引的存储结构。关键区别在于:
这是面试中的高频考点:
-- 创建测试表
CREATETABLEuser (
idBIGINT PRIMARY KEY, -- 主键,不允许 NULL
email VARCHAR(50) UNIQUE, -- 唯一索引,允许 NULL
phone VARCHAR(20) UNIQUE -- 唯一索引,允许 NULL
);
-- 主键测试:插入 NULL 会报错
INSERTINTOuser (id, email) VALUES (NULL, 'test@qq.com');
-- ❌ ERROR 1048: Column 'id' cannot be null
-- 唯一索引测试:允许 NULL,且 MySQL 中可以插入多个 NULL
INSERTINTOuser (id, email) VALUES (1, NULL); -- ✅ 成功
INSERTINTOuser (id, email) VALUES (2, NULL); -- ✅ 成功(MySQL 认为多个 NULL 不重复)
INSERTINTOuser (id, email) VALUES (3, 'a@qq.com'); -- ✅ 成功
INSERTINTOuser (id, email) VALUES (4, 'a@qq.com'); -- ❌ Duplicate entry
img
关键结论:
NULL != NULL)通过主键查询 vs 通过唯一索引查询的性能差异:
-- 表结构
CREATETABLEuser (
idBIGINT PRIMARY KEY,
email VARCHAR(50) UNIQUE,
nameVARCHAR(50),
age INT
);
-- 场景 1:通过主键查询
SELECT * FROMuserWHEREid = 1;
-- ✅ 直接走聚簇索引,一次查询即可获取完整数据
-- 场景 2:通过唯一索引查询所有字段
SELECT * FROMuserWHERE email = 'test@qq.com';
-- ⚠️ 需要回表:先查唯一索引得到 id,再回表查聚簇索引
-- 场景 3:通过唯一索引查询索引列(覆盖索引)
SELECT email FROMuserWHERE email = 'test@qq.com';
-- ✅ 覆盖索引,不需要回表
img
场景 | 推荐索引类型 | 原因 |
|---|---|---|
标识每一行记录 | 主键索引 | 每表必须有唯一标识,推荐自增 BIGINT |
用户邮箱不能重复 | 唯一索引 | 业务唯一性约束,允许未设置邮箱(NULL) |
手机号唯一 | 唯一索引 | 允许用户暂未绑定手机号 |
身份证号唯一 | 唯一索引 | 允许 NULL(可能未录入) |
联合唯一(用户 + 日期) | 联合唯一索引 | UNIQUE KEY uk_user_date (user_id, date) |
最佳实践:
-- 推荐:使用 BIGINT 自增主键
CREATETABLE orders (
idBIGINTUNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(32) UNIQUENOTNULL, -- 业务订单号,不允许 NULL
user_id BIGINTNOTNULL,
INDEX idx_user (user_id)
);
-- 不推荐:没有主键的表
-- MySQL 会自动选择一个非空唯一索引作为聚簇索引
-- 如果没有合适的,会生成一个隐藏的 6 字节主键InnoDB 要求每张表必须有聚簇索引:

img
ROW_ID。但强烈不建议这样做,应该显式定义主键。主键 vs 唯一索引:
主键索引是 聚簇索引,每表只能有一个,不允许 NULL,叶子节点存储完整行数据;唯一索引是 二级索引,可以有多个,允许 NULL(可多个),叶子节点存储主键值。查询时主键直接获取数据,唯一索引需要回表。推荐使用 BIGINT 自增主键,业务唯一约束用唯一索引。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。