怎么在MySQL中创建联合主键并理解对存储引擎底层的影响

浅芳酱_2287

浅芳酱_2287

2026-10-30

259人浏览

原创

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

怎么在mysql中创建联合主键并理解对存储引擎底层的影响

联合主键不是“多个主键”,而是单个索引覆盖多列

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 的查询基本无效。

  • 联合主键无法使用自增特性: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):

kashen-mysql DDLtoExcel
kashen-mysql DDLtoExcel

解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。

下载
  • 走主键查询(如 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+树、二级索引有多大。选错联合主键,优化器连“走哪个索引”都可能误判,而且后期几乎无法在线修改。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4643

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

951

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1209

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6621

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3363

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6600

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

9081

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1230

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1072

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 193人学习