主键是innodb聚簇索引的锚点,决定数据物理存储顺序;应优先选用auto_increment整数,避免uuid或业务字段,无主键将导致性能劣化与功能受限。

MySQL主键不是“随便选一个唯一字段就行”,它是InnoDB表数据物理组织方式的锚点——选错主键,等于给整张表埋下性能隐患。
主键就是聚簇索引,不是“加个约束”那么简单
在InnoDB中,主键直接决定数据怎么存:表数据就长在主键索引的叶子节点上。没主键?InnoDB会偷偷生成一个隐藏的GEN_CLUST_INDEX;有主键但选得差?比如用UUID或varchar(255)当主键,B+Tree页分裂变频繁,写入吞吐掉30%以上很常见。
- 主键=聚簇索引=数据存储顺序,三者绑定,改主键值实际是移动整行数据
- 二级索引(比如
INDEX idx_email(email))的叶子节点存的不是行指针,而是主键值——主键越长,所有二级索引体积越大 - 查询
WHERE id = ?最快,因为直接走聚簇索引;而WHERE email = ?要先查二级索引拿到id,再回表查数据,多一次B+Tree查找
AUTO_INCREMENT整数主键为什么是默认首选
不是因为它“简单”,而是它天然匹配InnoDB的B+Tree写入模式:单调递增、定长、紧凑。
-
INT UNSIGNED AUTO_INCREMENT占4字节,BIGINT占8字节——够用就好,别盲目升BIGINT,否则二级索引膨胀明显 - 自增ID插入时基本顺序追加,极少触发页分裂;而
UUID十六进制字符串无序,插入位置随机,页分裂率飙升 - 分布式场景下确实不能单靠
AUTO_INCREMENT,但替代方案应是雪花算法ID(64位整数),而非UUID——后者36字符,主键索引体积是前者的9倍以上 - 不要用业务字段当主键,比如
order_no或phone:一旦规则变更(如订单号格式升级),改主键代价极高,且可能破坏外键引用
没有主键的表,正在悄悄拖慢你的查询
很多人以为“小表不用主键也无所谓”,但InnoDB会强制补一个隐藏rowid,这个rowid不对外暴露,也没法被任何SQL引用,导致你根本没法高效定位单行。
- 没主键的表,
SELECT * FROM t WHERE pk = ?这种语句根本不存在——你只能全表扫描或依赖二级索引回表 - 外键必须引用主键,没主键就无法建外键,参照完整性只能靠应用层硬扛
- 备份恢复、主从同步、Online DDL操作都更脆弱,某些工具甚至拒绝处理无主键表
- 检查方法:
SHOW CREATE TABLE t;看输出里有没有PRIMARY KEY;或者SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't' AND TABLE_ROWS > 0 AND TABLE_NAME NOT IN (SELECT TABLE_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE CONSTRAINT_NAME = 'PRIMARY');
真正容易被忽略的点:主键设计不是建表时点一下就完事,它决定了未来三年这张表的读写瓶颈在哪里。哪怕现在数据才几百行,只要它会长期存在、会被关联、会被高频查询,主键就必须按InnoDB的物理逻辑来选——而不是按业务理解里的“哪个字段看起来最唯一”。











