UUID 生成是什么
UUID 用来生成全局唯一标识,不依赖数据库自增序列,适合分布式系统、分库分表与需要在写库前就确定主键的场景。MySQL 提供 UUID() 与 UUID_TO_BIN(),返回的是字符串形式(UUID() 生成的是基于时间与机器的 v1 形式,同时也可用 UUID_SHORT() 取更短的数字);PostgreSQL 早期用 uuid-ossp 扩展的 uuid_generate_v4(),13 起内置 gen_random_uuid()。存储用字符串直观,用二进制更省空间,代价是可读性下降。
基本信息
| MySQL 函数 | UUID() 返回字符串,UUID_SHORT() 返回递增整数 |
|---|---|
| MySQL 类型 | 可用 CHAR(36) 存字符串,或 BINARY(16) 存二进制 |
| PostgreSQL 生成 | gen_random_uuid()(13 起内置),更早版本需 uuid-ossp 扩展 |
| PostgreSQL 类型 | uuid 原生类型,占 16 字节 |
| 主键特点 | 随机分布导致写入热点分散,但索引页分裂更多 |
| 适用场景 | 分布式写入、跨库主键、写入前需知主键 |
典型使用场景
- 分库分表主键:不依赖某个库的序列
- 写入前需确定主键:先拿到 ID 再上传附件或写关联表
- 跨系统数据合并:避免不同来源主键冲突
- 外部可见标识:用不可猜测的 ID 替代自增编号暴露
- 临时对象标识:任务、上传文件的唯一键
- 多租户隔离:把租户 ID 与 UUID 组合作为主键
示例
示例
-- MySQL:生成与存储
SELECT UUID(); # 形如 550e8400-e29b-...
CREATE TABLE t (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), name VARCHAR(50));
-- 需要更紧凑时可以转成二进制存储
SELECT UUID_TO_BIN(UUID()); # 16 字节
SELECT BIN_TO_UUID(id) FROM t; # 读回时还原为可读形式
-- PostgreSQL:13 及以上内置
SELECT gen_random_uuid();
CREATE TABLE t (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text);
-- 更早版本需要先安装扩展
-- CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- SELECT uuid_generate_v4();存储方式对照
| 方式 | 占用 | 特点 |
|---|---|---|
| CHAR(36) | 36 字节 | 可读性最好,占空间 |
| BINARY(16) | 16 字节 | 省 60% 左右空间,需转换 |
| uuid(PostgreSQL) | 16 字节 | 原生类型,比较高效 |
| UUID_SHORT()(MySQL) | 8 字节 | 单调递增,基于服务器 ID 与时间 |
| 自增 ID | 4 或 8 字节 | 最短,但依赖单库序列 |
随机 UUID 作为主键会让插入位置分散,索引页分裂更多、顺序扫描变差;写入量大的表可考虑有序 UUID(如时间前缀)或自增主键加唯一业务键的组合。
MySQL 的 UUID() 生成的是 v1 形式,含时间与机器信息;如果对外暴露 ID 且不希望泄露这些信息,应换成随机版本或额外加业务标识。
存储类型与生成函数要配套:把 BINARY(16) 的列直接与字符串比较会得到空结果,读写两端都要显式转换。
使用要点
- 先确认是否需要全局唯一:单库场景自增主键往往更省空间
- 选择存储类型时权衡可读性与空间占用
- 读写两端保持一致:写了二进制就用转换函数读
- 把 UUID 当外部标识时,考虑是否需要不可猜测性
- 不要用 UUID 做排序键,随机分布会让排序与分页代价变高
- 在应用层生成 UUID 也可以,但要统一生成规则与版本