You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.6 KiB
3.6 KiB
ADR 001: 采用 UUID v7 作为数据库主键
状态: 已接受
日期: 2025-01-18
决策者: 架构团队
背景
项目初期使用自增整数(int)作为数据库主键。随着系统架构演进,需要重新评估主键策略,以支持:
- 分布式系统扩展
- 数据安全性(防止 ID 枚举攻击)
- 跨服务数据同步
- 更好的索引性能
决策
采用 UUID v7 作为所有业务表的主键。
UUID v7 特性
- 时间排序性:包含 Unix 时间戳(毫秒级),天然有序
- 索引友好:相比 UUID v4,索引性能更优(减少页分裂)
- 分布式友好:无需中心化 ID 生成器
- 安全性:不可预测,防止 ID 枚举
- 兼容性:符合 RFC 4122 标准
技术实现
后端(Python):
from uuid6 import uuid7
def generate_uuid() -> str:
return str(uuid7())
数据库(PostgreSQL):
- 存储类型:
VARCHAR(36)或原生UUID - 索引:B-Tree 索引(UUID v7 有序性保证良好性能)
前端(TypeScript):
interface Entity {
id: string; // UUID v7
}
替代方案
方案 A:保持 int 自增 ID
优点:
- 简单直观
- 占用空间小(4-8 字节)
- 查询性能略优
缺点:
- 不适合分布式系统
- 存在 ID 枚举风险
- 跨库合并困难
方案 B:UUID v4(随机)
优点:
- 完全随机,安全性高
- 分布式友好
缺点:
- 无序,索引性能差
- 页分裂严重
方案 C:Snowflake ID
优点:
- 有序,性能好
- 包含时间戳
缺点:
- 需要中心化服务
- 依赖机器 ID 配置
- 复杂度高
影响
Breaking Changes
- API 响应:所有 ID 字段从
number改为string - 数据库迁移:需要重建表或迁移数据
- 前端类型:需要更新 TypeScript 类型定义
迁移策略
由于项目处于初期开发阶段,采用激进迁移:
- 立即修改所有模型为 UUID v7
- 清空开发数据库
- 更新前端类型定义
- 无需数据迁移脚本
性能影响
- 存储空间:UUID(36 字节)vs int(4-8 字节),增加约 4-8 倍
- 索引性能:UUID v7 有序性保证 B-Tree 索引性能接近 int
- 查询性能:字符串比较略慢于整数,但差异可忽略(< 5%)
开发体验
- 调试:UUID 更难记忆,但可通过工具辅助
- 日志:UUID 更长,但包含时间信息,便于追踪
- API 测试:需要使用真实 UUID,不能用 1、2、3 等简单值
实施计划
阶段 1:基础设施(已完成)
- 添加
uuid6依赖 - 创建
id_generator.py工具
阶段 2:模型迁移(已完成)
- User 模型改为 UUID v7
- UserSession 模型改为 UUID v7
- Folder 模型使用 UUID v7
阶段 3:前端适配(已完成)
- 更新
types/user.ts - 更新
types/folder.ts
阶段 4:文档更新(已完成)
- 创建 ADR 文档
- 更新需求文档
监控指标
迁移后需要监控:
- 查询性能:对比迁移前后的查询耗时
- 索引大小:监控索引空间占用
- API 响应时间:确保无明显性能下降
参考资料
决策结果:✅ 已采纳并实施