auto_increment主键比uuid更优,因其顺序插入避免页分裂、提升缓存局部性、降低i/o开销;uuid随机性导致索引碎片率高3倍、范围查询退化为随机io、存储膨胀且易因传参不匹配导致索引失效。

MySQL
编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。
下载
AUTO_INCREMENT 主键在绝大多数场景下比
UUID() 更适合做 MySQL 的主键,核心原因不是“UUID 不够唯一”,而是它直接破坏了 InnoDB 聚簇索引的物理组织逻辑。
自增ID插入不触发页分裂,UUID几乎必然触发
InnoDB 的聚簇索引把数据行和主键值绑在一起存,B+ 树叶子节点必须按主键顺序链接。
AUTO_INCREMENT 总是追加到末尾,新页分配简单、连续,
INSERT 延迟稳定在 0.2–0.5ms;
UUID() 返回的是随机字符串(如
'550e8400-e29b-41d4-a716-446655440000'),每次插入都要搜索整棵树定位位置,大概率落在中间页——已有页填满就拆,指针重连,I/O 成倍增加。
实测百万级写入,
UUID 表的
Data_free 值(SHOW TABLE STATUS 输出)通常是自增表的 3 倍以上,说明碎片严重。
UUID主键让范围查询退化为随机IO
WHERE id BETWEEN 1000 AND 2000 这类查询,在自增主键下只需读取连续几个数据页;
换成
UUID 主键,逻辑上“相邻”的 ID 在磁盘上可能散落在数百个不同页里,变成大量随机读,缓存命中率暴跌。
更隐蔽的问题是:ORM 或应用层传参稍有不慎(比如传
'9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d' 漏横杠,或大小写混用),
WHERE id = ? 就完全走不了索引,执行计划里
type 显示
ALL 而不是
const。
存储空间膨胀影响所有索引
主键不只是自己占空间,它还被所有二级索引作为“行指针”重复引用。
BIGINT 主键只占 8 字节,
CHAR(36) 却占 36 字节——单表 1000 万行,主键索引就多占约 280MB;若有 5 个二级索引,总膨胀接近 1.4GB。
即使改用
BINARY(16) +
UUID_TO_BIN(UUID(), TRUE),仍比
BIGINT 多出 8 字节,且无法解决根本的随机性问题。
真正需要 UUID 的场景极少,基本只有两个:分库分表又没全局序列服务、或多库数据要合并且无法重映射主键。其他所谓“防爬”“安全”“分布式友好”,都该用映射表或前端脱敏来解,而不是拿索引性能换。