专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

数据库主键设计与避免重复策略_数据库主键如何设计

ins518 2025-09-01 03:56:43 技术文章 6 ℃ 0 评论

哈喽,你好啊,我是雷工!

最近在研究数据库设计表格时,用到主键的设计。

由于没有系统的学习过相关知识,一时不知道该如何设计主键,

于是遇事不决问AI,决定通过问AI的形式学习一下该如何设计主键,并避免重复策略。

01 如何提问

问题如下:

设计数据库时主键该如何设计,用什么作为主键,而且如何避免重复?

02 解决方案

以下是AI给出的回答:

设计数据库主键是一个核心问题,直接影响数据完整性、查询性能、扩展性和维护性。以下是主键设计的核心原则、常见方案以及避免重复的策略:

一、主键的核心要求

  1. 唯一性:在整个表中绝对唯一,不能重复。
  2. 非空性:不能为 NULL。
  3. 稳定性:值一旦生成,最好永不改变(避免外键引用失效)。
  4. 简洁性:尽量短小(利于索引效率和外键存储)。
  5. 简单性:最好是单一字段(非强制,但复合主键会增加复杂度和外键引用难度)。

二、常见主键设计方案及优缺点

方案

优点

缺点

适用场景

1. 自增整数 (AUTO_INCREMENT / SERIAL)

简单易用<br> 存储空间小<br> 索引效率高<br> 天然保证唯一性

无业务含义<br> 分库分表时需额外处理<br> 易暴露数据量<br> 插入前无法预知ID

单机/简单系统、OLTP 核心表、不关心业务含义的场景

2. UUID/GUID

全局唯一(跨库/跨服务器)<br> 生成无需依赖数据库<br> 隐藏数据量

存储空间大 (32字符)<br> 索引效率低(随机写入)<br> 无序性导致页分裂

分布式系统、需要提前生成ID、数据合并场景

3. 雪花算法 (Snowflake ID)

全局唯一、大致有序<br> 存储空间适中 (64-bit)<br> 高性能

实现复杂(需维护机器ID)<br> 依赖系统时钟(时钟回拨问题)

高并发分布式系统、需要全局有序ID的场景

4. 业务主键 (Natural Key)

有业务含义<br> 可读性强<br> 可能减少关联查询

可能变更(违反稳定性)<br> 可能过长<br> 组合键复杂<br> 唯一性难保证

编码规则严格且不变的场景(如身份证号、ISBN)

5. 混合方案 (业务键+代理键)

平衡业务需求和稳定性

增加冗余字段<br> 设计稍复杂

既需业务标识又需稳定主键的场景

三、如何避免主键重复 - 关键策略

  1. 使用数据库自增机制:
  2. AUTO_INCREMENT (MySQL), SERIAL (PostgreSQL), IDENTITY (SQL Server), Sequence (Oracle, PostgreSQL)。
  3. 优点:数据库内部保证唯一性,绝对可靠。
  4. 缺点:仅适用于单库或主从复制环境,分布式环境下需特殊方案(如设置不同初始值/步长 - 不推荐)。
  5. 使用标准化全局唯一生成器:
  6. UUID/GUID:使用标准库生成(如 UUID.randomUUID())。理论上重复概率极低(2^128),可认为唯一。
  7. 雪花算法 (Snowflake):确保机器ID(workerId)在集群内唯一,解决时钟回拨问题(时钟回拨可能导致重复)。
  8. Redis 自增:利用 Redis 的原子操作 INCR 生成集群范围内唯一的序列号。
  9. 数据库 Sequence 服务:专门提供分布式 ID 生成服务(如 TDDL Sequence、Leaf)。
  10. 业务主键的严格校验:
  11. 数据库唯一约束:在业务字段上创建 UNIQUE 约束,作为最后防线。
  12. 应用层校验:插入/更新前检查该业务值是否存在(注意并发问题!需结合数据库唯一约束)。
  13. 预生成与分配:由中心系统按规则生成并分配业务主键值(如订单号生成服务)。
  14. 分布式数据库的特殊支持:
  15. NewSQL 数据库(如 TiDB、CockroachDB)提供 AUTO_RANDOM 或分布式序列机制。

四、关键设计原则与建议

  1. 优先选择代理键: 95% 的场景推荐使用 自增ID(单机)或 雪花ID/UUID(分布式)作为主键。它们稳定、简单、解耦业务。
  2. 避免使用业务主键: 除非该业务属性 绝对稳定、简短且唯一性可保证(如身份证号,但需注意隐私和变更情况)。
  3. 禁止使用可能变更的字段: 如用户名、邮箱、公司名称等。
  4. 避免大字段做主键: 如 TEXT, BLOB,严重影响索引性能。
  5. 复合主键谨慎使用: 增加复杂度,影响关联表设计。仅在多对多关系表或强业务约束下使用。
  6. 显式定义主键: 即使有唯一索引,也应显式定义 PRIMARY KEY,优化器会优先使用。
  7. 主键就是聚簇索引(多数DBMS): 主键顺序影响物理存储(InnoDB)。有序ID(自增、雪花)插入性能远高于随机ID(UUID)。
  8. UUID 优化:
  9. 使用 UUIDv7(时间戳有序)代替 v4(完全随机)。
  10. 存储时转换为 BINARY(16) 减少空间(如 MySQL 用 UNHEX(REPLACE(UUID(), '-', '')))。

五、示例选择指南

  • 用户表 (user):id BIGINT AUTO_INCREMENT 或 id BIGINT(雪花ID)
    • 绝对不要用 username 或 email 做主键!(可能变更,且较长)
  • 订单表 (order):order_id VARCHAR(32)(包含日期+序列号的业务号,并额外加一个 id BIGINT AUTO_INCREMENT/雪花 作为主键
    • 业务号可做唯一索引,主键用代理键。
  • 国家表 (country):country_code CHAR(2) PRIMARY KEY(ISO 3166-1 标准,稳定且短小)
  • 用户-角色关联表 (user_role):(user_id, role_id) PRIMARY KEY(经典复合主键场景)

总结:最佳实践路径

  1. 默认选择: 单机/简单系统 → 自增整数 (AUTO_INCREMENT)。分布式系统 → 雪花算法 (Snowflake)
  2. 特殊需求: 需要提前生成ID → UUIDv7。严格业务标识 → 业务键+代理键+唯一约束
  3. 绝对底线:主键必须唯一、非空、稳定。无论选哪种方案,最终一定要在数据库层通过 PRIMARY KEY 约束来保证唯一性,这是最后的堡垒。

通过遵循这些原则,你就能设计出既高效又健壮的主键结构,为数据库的稳定性和扩展性打下坚实基础!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表