先澄清一个概念:主键 id 本身当然可以作为数据库主键。通常说“id 不能用到业务中”,意思是:不要让数据库的技术主键 id 直接承担业务含义,或不要把业务字段当主键。也就是要把“技术主键”和“业务标识”分开。
一、设计表时,主键 id 的作用
唯一标识一行数据
每行记录都有一个稳定、唯一的身份,避免靠多个字段组合才能定位。
保证实体完整性
主键非空、唯一,数据库层面防止重复记录。
作为外键关联依据
子表通过 user_id、order_id 关联父表,保持引用完整性。
提高查询和更新效率
WHERE id = ? 能快速定位记录。InnoDB 中主键还是聚簇索引,决定数据物理组织方式。
影响索引和存储性能
二级索引通常存储主键值。主键越短、越有序,索引越小,插入越稳定。自增 bigint 通常比 UUID 更适合做 InnoDB 主键。
支撑 ORM、分页、复制、分片、迁移
ORM 映射、游标分页、数据同步、分库分表、数据合并都需要一个稳定的唯一 ID。
隔离业务变化
业务字段会变,技术主键不变。业务规则调整时,不影响表之间的关联。
二、为什么 id 不适合“运用到业务中”
这里的 id 通常指自增主键、数据库内部 ID。它适合做技术主键,但不适合直接当业务标识。
没有业务含义
用户看到订单 id 1001,不知道它代表什么。业务需要的是订单号、用户编号、学号这类可读、可校验的标识。
容易被枚举和攻击
自增 id 可预测,/orders/1001 改成 /orders/1002 就能遍历。容易造成数据泄露、越权访问,即 IDOR 漏洞。
数据库迁移或分库分表时会变/冲突
单库自增 id 在多库、多服务、数据合并时可能重复。业务不能依赖这种可能变化的内部 ID。
业务字段做主键不稳定
手机号会换绑,身份证会升位,邮箱会变,订单号格式可能调整。主键一旦更新,所有外键、索引都要跟着变,代价很大。
性能和存储问题
如果用手机号、身份证、UUID 等长业务字段做主键,InnoDB 二级索引会变大,插入可能随机,导致页分裂和查询变慢。
隐私和合规风险
身份证、手机号、邮箱做主键,会把这些敏感信息扩散到大量关联表和索引中,泄露风险高,也不利于匿名化。
业务唯一性范围不同
业务字段可能只是“租户内唯一”“机构内唯一”,而主键通常要求全表甚至全局唯一。
插入前拿不到 id,不利于幂等
自增 id 由数据库生成,插入前不知道。业务幂等通常需要业务请求号、订单号等提前生成的业务键。
三、正确做法
通常采用:
- 技术主键:id bigint unsigned auto_increment 或雪花 ID、UUID,无业务含义。
- 业务唯一键:如 order_no、user_no、phone,加唯一索引。
- 对外展示:用业务编号、UUID、加密 ID,不直接暴露自增 id。
- 内部关联:外键仍用 id,保证效率和稳定。
示例:
CREATE TABLE orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, — 技术主键
order_no VARCHAR(32) NOT NULL, — 业务编号
user_id BIGINT UNSIGNED NOT NULL,
amount DECIMAL(10,2),
UNIQUE KEY uk_order_no (order_no),
KEY idx_user_id (user_id)
);
对外用 order_no,内部关联用 id。
总结:主键 id 的职责是稳定、唯一、高效地标识数据库内部记录;业务标识的职责是可读、可校验、可展示、可变更。两者最好分开。 不是 id 不能做主键,而是不要让数据库主键 id 直接承担业务含义。
网硕互联帮助中心



评论前必须登录!
注册