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

ADR 001: 采用 UUID v7 作为数据库主键

状态: 已接受
日期: 2025-01-18
决策者: 架构团队


背景

项目初期使用自增整数(int)作为数据库主键。随着系统架构演进,需要重新评估主键策略,以支持:

  1. 分布式系统扩展
  2. 数据安全性(防止 ID 枚举攻击)
  3. 跨服务数据同步
  4. 更好的索引性能

决策

采用 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

  1. API 响应:所有 ID 字段从 number 改为 string
  2. 数据库迁移:需要重建表或迁移数据
  3. 前端类型:需要更新 TypeScript 类型定义

迁移策略

由于项目处于初期开发阶段,采用激进迁移

  1. 立即修改所有模型为 UUID v7
  2. 清空开发数据库
  3. 更新前端类型定义
  4. 无需数据迁移脚本

性能影响

  • 存储空间: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 文档
  • 更新需求文档

监控指标

迁移后需要监控:

  1. 查询性能:对比迁移前后的查询耗时
  2. 索引大小:监控索引空间占用
  3. API 响应时间:确保无明显性能下降

参考资料


决策结果 已采纳并实施