Skip to content

数据库设计

参考:From Idea to Production-Ready Database Design

以社交动态应用为例,数据库设计按「需求 → ERD → 表 → 外键 → 多对多关系」推进。先表达业务关系,再落到数据库实现。

明确需求

先列出应用必须支持的功能。例如:

  • 用户注册、登录并维护个人资料。
  • 用户发布动态,其他用户可以评论。
  • 用户可以关注其他用户。

从需求中提取名词和行为:UserTweetComment 是候选实体;发布、评论、关注提示了实体间的关系。

绘制实体关系图

先标注实体及关系基数,不急于编写建表语句:

md
User 1 ──── N Tweet
Tweet 1 ──── N Comment
User 1 ──── N Comment
User N ──── N User(关注)

一对多关系中,外键位于「多」的一方;用户关注用户属于自关联的多对多关系。

定义实体与主键

每个实体都需要稳定且唯一的主键。时间戳用于保留记录创建和更新的时间;用户名、邮箱等业务字段是否唯一,应由实际需求决定。

ts
User {
  id: string pk
  username: string
  email: string
  createdAt: timestamp
  updatedAt: timestamp
}

Tweet {
  id: string pk
  content: string
  createdAt: timestamp
  updatedAt: timestamp
}

Comment {
  id: string pk
  content: string
  createdAt: timestamp
  updatedAt: timestamp
}

不要把可能变化的业务值当作主键;主键只负责唯一识别一条记录。

补全关系与外键

将 ERD 变为关系表时,把外键放在引用另一条记录的表中。

ts
Tweet {
  id: string pk
  content: string
  userId: string fk // 发布该动态的用户
  createdAt: timestamp
  updatedAt: timestamp
}

Comment {
  id: string pk
  content: string
  userId: string fk // 发表评论的用户
  tweetId: string fk // 被评论的动态
  createdAt: timestamp
  updatedAt: timestamp
}

User → TweetTweet → Comment 都是一对多:一条动态可以有多条评论,因此 tweetId 应位于 Comment,而不是 Tweet

处理多对多关系

用户之间的关注不能在 User 中保存一组 ID,应单独创建关联表:

ts
Follow {
  followerId: string fk  // 发起关注的用户
  followingId: string fk // 被关注的用户
  createdAt: timestamp
}

Follow(followerId, followingId) 应唯一,避免同一用户重复关注同一目标;两个字段也共同描述了一条关注关系。

检查设计

  • 每个实体是否都有主键。
  • 每个一对多关系的外键是否放在「多」的一方。
  • 多对多关系是否通过关联表表达。
  • 是否保存了可由其他数据推导出的重复数据。
  • 是否为后续查询保留了必要的时间和关联字段。

基于 MIT 许可发布