为什么推荐使用自增主键?使用UUID作为主键的优缺点是什么?

为什么推荐使用自增主键?使用UUID作为主键的优缺点是什么?


推荐自增主键的五大原因

先记住一个前提:InnoDB 是聚簇索引,整张表的数据就是按主键顺序存在 B+ 树叶子页里的。这是所有区别的根源。

1. 顺序追加写入,大幅减少页分裂

sql 复制代码
CREATE TABLE user_auto (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

自增 ID 持续递增,新数据永远追加在数据页末尾。页面填满后新开一页就行,几乎不会触发页分裂。

通俗比喻:就像往文件夹里按 1、2、3 的顺序放文件,永远往后塞;UUID 是随机往里插,文件夹撑破了就要拆分、挪数据,大量消耗磁盘 IO,产生索引碎片。

2. 页面填充率高,磁盘空间利用率好

顺序写入页面填充率接近 15/16;UUID 随机插入频繁页分裂,页面经常只填充一半,大量磁盘空间闲置浪费。

3. 主键连续,范围查询和排序性能极强

日常高频 SQL:id > 100 AND id < 200,或者 ORDER BY id DESC LIMIT 10。自增主键在磁盘上物理连续,走顺序 IO,直接扫过去。

UUID 没规律,ORDER BY uuid 变成随机 IO,磁盘头来回跳,性能差距明显。

4. 主键字段更小,二级索引整体瘦身,B+ 树更矮

BIGINT 只占 8 字节;字符串 UUID 占 36 字节,二进制 UUID 也要 16 字节。

InnoDB 所有二级索引的叶子节点都存主键值。主键越大,索引越胖。一张表 5 个二级索引,千万级数据可能多出几百 MB。

更重要的是:主键越小,B+ 树每层能放下的索引条目越多,树的高度就越低 。同样是千万级数据,8 字节主键可能树高 3 层,36 字节 UUID 可能树高 4 层。多一层就多一次磁盘 IO

5. 自增锁机制完善,高并发写入稳定

MySQL 默认 innodb_autoinc_lock_mode = 1,单行插入几乎无锁,批量插入也做了轻量优化,并发写入吞吐稳定。


UUID 做主键的优缺点

屏幕中栏贴两套建表 SQL,上半部分讲优点,下半部分标红讲缺点。

两种 UUID 建表写法

sql 复制代码
-- 普通字符串 UUID,可读性强,但最占空间
CREATE TABLE user_uuid_str (
    id CHAR(36) PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB;

-- MySQL 推荐二进制存储,省一半空间
CREATE TABLE user_uuid_bin (
    id BINARY(16) PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB;

UUID 三大优点

  1. 天然全局唯一:分库分表、多服务分布式架构下,各节点生成 ID 不会重复,不用依赖数据库发号。
  2. 防爬虫泄露业务体量 :自增 ID 容易被遍历,/order/1/order/2 一眼看出订单量;UUID 无规律,安全性更高。
  3. 去中心化生成:应用层直接生成,不访问数据库,减轻 DB 压力。

UUID 五大硬伤(重点标红)

  1. 写入随机,页分裂和索引碎片严重

    常规 UUID 随机分布,插入位置不可控,数据量大后碎片率极高,频繁 OPTIMIZE TABLE 都很难修复。

    补充知识点 :UUID v1 带时间戳,是有序的,可以缓解页分裂。但它自带物理 MAC 地址,有隐私泄露风险,业务上极少使用。

  2. 字段体积大,索引膨胀严重

    字符串 UUID 是 BIGINT 的 4 倍多,二级索引跟着一起胖,磁盘和内存占用双双上涨。

  3. 无法利用磁盘顺序 IO 和 CPU 缓存

    主键无序,缓存命中率低。精准主键查询还行,范围查询和排序性能拉胯。

  4. 主键没有顺序业务价值

    ORDER BY id 对 UUID 来说完全是随机排序,不像自增 ID 能反映创建时间,没法靠 ID 做时间分页。

  5. 高并发写入拖累 TPS

    随机写频繁刷新缓冲池,热点数据页来回换入换出,数据库吞吐明显下降。


选型决策树

使用场景 首选主键方案 选型逻辑
单机单库、不分片 BIGINT 自增 性能、空间、维护全最优
分布式、分库分表 雪花算法(Snowflake) 趋势递增 + 全局唯一,兼顾自增和 UUID 的优点
必须去中心化、不能依赖发号器 BINARY(16) UUID 二进制压缩,减少空间损耗
防爬、隐藏业务数据 UUID / 雪花 无规律 ID,防止批量遍历

如果必须用 UUID,至少做这个优化

sql 复制代码
-- 插入:字符串 UUID 转二进制,36 字节变 16 字节
INSERT INTO user_uuid_bin VALUES (UUID_TO_BIN(UUID()), 'Alice');

-- 查询:二进制转回可读字符串
SELECT BIN_TO_UUID(id), name FROM user_uuid_bin;

二者没有绝对优劣,核心看部署架构。单机优先自增,分布式优先雪花算法;UUID 只在强去中心化、隐私防爬时兜底使用,一定要用二进制压缩存储。


额外拓展

第一,线上禁止直接用 CHAR(36) UUID 做主键,实在要用必须 BINARY(16)

第二,分布式不要直接上 UUID,优先雪花算法、百度 UidGenerator 这类有序全局 ID

第三,所有隔离级别下,自增主键写入性能都稳定,UUID 则全级别都有 IO 损耗。

相关推荐
就叫_这个吧3 小时前
Java递归方法实现面包屑导航
java·开发语言
To_OC3 小时前
面试被问了三回三栏布局,这次我终于把 BFC 那层窗户纸捅破了
前端·css·面试
fīɡЙtīиɡ ℡3 小时前
AI 应用系统设计
java·开发语言·人工智能
Lonely 净土3 小时前
Linux 运维文件写入操作
linux·运维·服务器·文件管理
城管不管3 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅3 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
倔强的石头1063 小时前
向量数据库从相似度检索走向融合数据底座
数据库
To_OC3 小时前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
AI绘画哇哒哒3 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
飞英思特无线充电4 小时前
严苛场景巡检机器人的无线补能设计:点位、BMS、认证与运维边界
运维·机器人