主题
数据库设计
参考:From Idea to Production-Ready Database Design
以社交动态应用为例,数据库设计按「需求 → ERD → 表 → 外键 → 多对多关系」推进。先表达业务关系,再落到数据库实现。
明确需求
先列出应用必须支持的功能。例如:
- 用户注册、登录并维护个人资料。
- 用户发布动态,其他用户可以评论。
- 用户可以关注其他用户。
从需求中提取名词和行为:User、Tweet、Comment 是候选实体;发布、评论、关注提示了实体间的关系。
绘制实体关系图
先标注实体及关系基数,不急于编写建表语句:
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 → Tweet 和 Tweet → Comment 都是一对多:一条动态可以有多条评论,因此 tweetId 应位于 Comment,而不是 Tweet。
处理多对多关系
用户之间的关注不能在 User 中保存一组 ID,应单独创建关联表:
ts
Follow {
followerId: string fk // 发起关注的用户
followingId: string fk // 被关注的用户
createdAt: timestamp
}Follow 的 (followerId, followingId) 应唯一,避免同一用户重复关注同一目标;两个字段也共同描述了一条关注关系。
检查设计
- 每个实体是否都有主键。
- 每个一对多关系的外键是否放在「多」的一方。
- 多对多关系是否通过关联表表达。
- 是否保存了可由其他数据推导出的重复数据。
- 是否为后续查询保留了必要的时间和关联字段。
