← 返回题目列表

数据库主键应该如何设计?自增 ID 和 UUID 怎么选?

高频 中等 第 19 / 33 题 更新于 2026/07/28
数据库设计主键自增IDUUID

简化版

主键应该稳定、唯一、尽量短,并且不包含会变化的业务含义。自增 ID 对 B+ 树和范围查询友好,但分布式生成和暴露业务规模要处理;UUID 全局生成方便,但较长且随机,可能增加索引空间和写入离散度。

详细版

主键设计原则:

  • 稳定:不要使用会变的手机号、邮箱、身份证状态字段做主键;
  • 唯一:必须能稳定标识一行;
  • 短小:InnoDB 二级索引会存主键值,主键越长,索引越大;
  • 无业务含义或弱业务含义:避免业务规则变化导致主键体系崩;
  • 适合写入模式:递增或趋势递增通常更适合聚簇索引。

常见选择:

类型优点缺点
自增 ID简单、短、索引友好分库分表下要解决全局唯一,可能暴露规模
UUID客户端可生成、全局唯一概率高长、随机、索引和存储成本高
雪花 ID趋势递增、分布式友好依赖时钟和机器号管理

完整版教学

一、主键不是随便找个唯一字段

主键是整张表的身份标识。它会被外键引用、被业务传递、被索引使用,也会影响数据物理组织方式。

一个好主键要长期稳定。如果用手机号做主键,用户换手机号怎么办?如果用邮箱做主键,邮箱改绑怎么办?业务字段会变化,就不适合做主键。

二、InnoDB 下主键还影响聚簇索引

InnoDB 表的数据按聚簇索引组织,通常就是主键索引。主键叶子节点存整行数据,二级索引叶子节点存二级索引列和主键值。

这意味着主键越长,所有二级索引都会被放大。比如用 36 字符 UUID 做主键,二级索引里也要保存这个长主键,缓存效率和存储成本都会受影响。

三、自增 ID 的优点和边界

自增 ID 短、递增、索引友好。插入时大多追加到 B+ 树右侧,页分裂和随机写压力相对小。

但自增 ID 也有问题:

  • 分库分表后各库自增会冲突;
  • 对外暴露可能泄露业务规模;
  • 多主写入或跨系统合并数据时不方便;
  • 高并发极端场景可能有自增锁或热点页压力。

这些问题通常通过号段、雪花 ID、隐藏内部 ID、对外使用短码等方式解决。

四、UUID 的优点和问题

UUID 最大优点是生成方便,不依赖数据库,跨系统合并也比较自然。

问题是它通常较长,而且随机分布。作为聚簇主键时,插入位置分散,可能增加页分裂和缓存压力。作为二级索引引用值时,也会放大索引体积。

如果必须使用 UUID,可以考虑:

  • 使用二进制存储而不是字符串;
  • 使用有序 UUID 或 ULID 等趋势有序方案;
  • 内部仍用自增或雪花 ID,对外暴露 UUID。

五、雪花 ID 为什么常见

雪花 ID 通常由时间戳、机器号、序列号组成。它的特点是分布式生成、趋势递增、长度相对可控,适合订单、消息、日志等高并发系统。

它的风险在于时钟回拨和机器号冲突。如果系统时钟倒退,可能生成重复或乱序 ID;如果机器号管理不严,也可能冲突。所以要有时钟保护和节点管理策略。

六、业务主键和数据库主键可以分开

有些业务字段也要求唯一,比如订单号、用户账号、手机号。这些可以建唯一索引,但不一定要作为数据库主键。

常见做法是:

id: 内部主键,短小稳定
order_no: 业务单号,唯一索引,对外展示

这样既保证数据库结构稳定,又满足业务查询和展示。

还有一个常见工程做法是“内部 ID 和外部 ID 分离”:内部 id 用短小趋势递增的 bigint,方便聚簇索引和关联;外部 public_idorder_no 用不暴露规模的编码,并建唯一索引。这样既保护内部存储效率,也满足对外安全和业务识别。

七、常见误区与追问

主键方案适合场景主要风险
自增 ID单库、写入顺序性要求高分布式唯一和规模暴露
UUID 字符串多端生成、跨系统合并长、随机、索引空间大
雪花 ID分布式高并发业务单号时钟回拨、机器号冲突

易错点:业务唯一字段可以建唯一索引,但不一定要当主键。主键优先服务“稳定标识一行”和“索引组织”,业务编码优先服务“对外识别和查询”。

主键长度对索引成本很敏感。假设一张表有 5 个二级索引、1000 万行,若主键从 8 字节 bigint 换成 36 字节 UUID 字符串,二级索引叶子节点里每行多约 28 字节主键引用,粗略就是:

28B * 10,000,000 * 5 ≈ 1.4GB

这还没算页目录、填充率和字符集开销,所以 InnoDB 表里长主键会实实在在放大二级索引和 Buffer Pool 压力。

  • 误区:只要字段唯一就适合做主键。 手机号、邮箱、身份证绑定状态都可能变化,更适合唯一索引,不适合做稳定主键。
  • 误区:UUID 永远比自增 ID 更安全。 UUID 不暴露规模,但作为聚簇主键可能带来更大的存储和随机写成本。
  • 误区:自增 ID 在分布式系统完全不能用。 可以通过号段、分库步长、发号服务等方案使用,只是要明确全局唯一策略。
  • 追问:为什么 InnoDB 主键要尽量短? 二级索引叶子节点会保存主键值,主键越长,所有二级索引都被放大。
  • 追问:雪花 ID 为什么是趋势递增而不是严格全局递增? 不同机器并发生成时整体大致按时间增长,但跨节点的精确顺序不应作为强业务语义依赖。
  • 追问:对外不想暴露自增 ID 怎么办? 内部用短主键,对外用订单号、短码、UUID 或加密 ID,并对外部字段建唯一索引。

八、加强记忆

主键要稳定、短小、唯一、少业务含义。自增 ID 简单且索引友好,UUID 生成方便但长且随机,雪花 ID 适合分布式但要处理时钟和机器号。业务唯一字段可以建唯一索引,不一定要当主键。