联合主键是单个覆盖多列的唯一聚簇索引,非多个主键;必须用PRIMARY KEY(a,b)单条声明,所有列自动NOT NULL,顺序影响B+树结构与查询性能。

联合主键不是“多个主键”,而是单个索引覆盖多列
MySQL里不存在“多个主键”的概念。所谓联合主键,就是用 PRIMARY KEY(a, b) 语法把两个或更多字段一起声明为主键,InnoDB 会将它们组合成一个**唯一的、不可为空的聚簇索引**。这个索引就是整张表唯一的聚簇索引——它既是主键,也决定了数据的物理存储顺序。
常见错误是以为加了 PRIMARY KEY(a) 又加 PRIMARY KEY(b) 就能实现联合效果,这在语法上直接报错:ERROR 1068: Multiple primary key defined。必须写成一条声明。
- 联合主键中的所有列都自动
NOT NULL,无需额外声明 - 列顺序至关重要:
PRIMARY KEY(user_id, order_time)和PRIMARY KEY(order_time, user_id)是两棵完全不同的B+树,查询效率差异可能极大 - 如果已有单列主键(比如自增
id),就不能再定义联合主键;想用业务字段组合做主键,必须先删掉原主键
联合主键强制使用 InnoDB,且彻底改变数据物理布局
MyISAM 不支持联合主键的语义约束(它允许重复、允许NULL),所以当你执行 CREATE TABLE ... PRIMARY KEY(a,b) 时,MySQL 会静默强制使用 InnoDB 引擎,哪怕你显式写了 ENGINE=MyISAM —— 这条建表语句会失败或被忽略。
更关键的是:联合主键作为聚簇索引,会让整张表的数据按 (a,b) 的字典序物理排序存储。例如:
INSERT INTO t (user_id, order_time, amount) VALUES (1001, '2026-10-10 10:00:00', 99), (1001, '2026-10-10 10:05:00', 120), (1002, '2026-10-10 09:30:00', 88);
这三行在磁盘上就严格按 (1001,'2026-10-10 10:00:00') → (1001,'2026-10-10 10:05:00') → (1002,'2026-10-10 09:30:00') 的顺序存放。这种布局对 WHERE user_id = ? AND order_time BETWEEN ? AND ? 这类查询极友好,但对仅查 order_time 的查询基本无效。
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
- 联合主键无法使用自增特性:
AUTO_INCREMENT只能作用于单列,且该列必须是联合主键的最左列(如PRIMARY KEY(id, user_id)中id可设自增,但user_id不行) - 没有显式主键时,InnoDB 会生成隐藏
rowid;但一旦定义了联合主键,这个隐藏ID就彻底消失,所有二级索引的叶子节点都存的是你定义的那组联合值(如(user_id, order_time)),而不是某个单独ID
联合主键 vs 普通联合索引:回表路径完全不同
这是最容易混淆的点。假设你有一张订单表,用 PRIMARY KEY(user_id, order_time),再建一个普通联合索引 KEY idx_status_time (status, order_time):
- 走主键查询(如
SELECT * FROM t WHERE user_id = 1001 AND order_time > '2026-10-01'):B+树一路走到叶子节点,数据就在那儿,零回表 - 走普通联合索引(如
SELECT * FROM t WHERE status = 'paid' AND order_time > '2026-10-01'):先在idx_status_time树里找到匹配的叶子节点,里面只存了(status, order_time, user_id)(注意:最后这个是主键值!因为主键是联合的,所以这里存的是完整联合主键值),然后再拿着这组值回到聚簇索引里去查整行——这个“拿着(user_id, order_time)去聚簇索引里再查一遍”的过程,就是回表,而且比单列主键回表开销更大
也就是说,普通联合索引的叶子节点存的是「联合主键全值」,不是某个ID。如果你的联合主键很长(比如含 UUID 字段),那每个二级索引的体积和回表成本都会显著上升。
什么时候该用联合主键?别为了“业务唯一性”硬套
联合主键真正值得用的场景非常有限,核心判断标准只有一条:**这张表是否天然就没有独立 ID,且业务逻辑上必须靠多字段组合才能定位一行,并且这些字段本身稳定、短小、有序**。
典型例子是关联表(如 user_role 表,只有 user_id 和 role_id 两列,且两者组合天然唯一)、或者时间分区明细表(如日志表按 date, log_id 主键,date 前缀保证写入局部性)。
- 不要因为“订单号+用户ID 组合唯一”就建联合主键:订单号本身已是全局唯一,加个自增
id主键更稳妥 - 避免用字符串、UUID、长文本字段参与联合主键:B+树内节点要存这些值,索引体积爆炸,范围扫描性能断崖下跌
- 如果后续可能需要按单个字段高频查询(比如只查
order_time),联合主键反而会让你被迫建一堆冗余二级索引,得不偿失
底层影响不是抽象概念:它直接决定磁盘怎么写、内存怎么缓、查询走几层B+树、二级索引有多大。选错联合主键,优化器连“走哪个索引”都可能误判,而且后期几乎无法在线修改。

















