7.9 数据库设计实战
Tip
糟糕的数据库设计是以后一切性能问题的根源。好的设计能让你的代码像呼吸一样自然。
1. 为什么要学这个?
老板让你做一个电商网站。为了省事,你在订单表里直接加了一个字符串字段:
items: "苹果, 香蕉, 橘子" (你想着反正存下来就行)。
一个月后,老板突然问:"上个月那种水果销量最高?" 你傻眼了。因为数据是一坨混在一起的文本,你必须写代码遍历几万条订单,逐个解析字符串,还要去重统计。 点击查询按钮,系统卡死了 30 秒,老板的脸色也黑了 30 秒。
掌握正确的关系设计,你的数据库不仅能存数据,还能像高智商大脑一样,毫秒级回答任何复杂的统计问题。
2. 核心概念
数据库设计的本质就是处理 "关系" (Relation)。现实世界只有三种关系:
1. 一对一 (1:1) —— 指纹识别
场景:用户 (User) 和 详细档案 (Profile)。 一个 User 只有一个 Profile。这就像每人只要一个指纹。
- Prisma 写法:不用数组,直接引用。
2. 一对多 (1:N) —— 部门领导
场景:用户 (User) 和 博客文章 (Post)。 一个 User 可以写 100 篇 Post,但一篇 Post 只能有一个作者。
- Prisma 写法:User 里有
Post[](数组),Post 里有User(单体)。
3. 多对多 (M:N) —— 进货单
场景:订单 (Order) 和 商品 (Product)。 一个 Order 里有多个 Product (苹果、香蕉)。 一个 Product 也可以出现在多个 Order 里 (张三买了苹果,李四也买了苹果)。
- Prisma 写法:必须引入第三者 —— 中间表 (Relation Table)。
3. 实战方案:设计电商系统
永远不要试图把所有东西塞进一张表。我们需要 "拆分" 和 "连接"。
错误的 "一表走天下"
// Order 表
{
"id": 1,
"user": "张三",
"items": ["苹果", "香蕉"] // ❌ 灾难的根源
}
正确的 "中间表架构"
我们需要一个中间人 OrderItem,它专门记录 "谁买了几个什么"。
erDiagram
User ||--o{ Order : "1. 下单"
Order ||--|{ OrderItem : "2. 包含"
Product ||--|{ OrderItem : "3. 对应"
Order {
int id
int userId
}
OrderItem {
int id
int orderId "归属订单"
int productId "对应商品"
int quantity "买了几个"
}
Product {
int id
string name
}
解读:
OrderItem 是最关键的桥梁。
- 它对 Order 说:"我是你的一部分"。
- 它对 Product 说:"我引用了你"。
这样,你想查"谁买了苹果",只需要问
OrderItem即可,这是毫秒级的索引查询。
4. 避坑指南
| ❌ 错误做法 | ✅ 正确做法 | 💡 代价 |
|---|---|---|
| JSON 存关系 | 使用中间表 | 本想省事,结果根本没法做统计查询 (如:过去7天销量 Top10),最后只能重构数据库。 |
| 硬删除 | 级联删除 (Cascade) | 删了用户,但他发的帖子还留在数据库里变成了"幽灵数据",前端一渲染就报错。 |
| 没建外键 | Prisma 自动管理 | 手动维护关联关系是非常容易出错的,让数据库替你守住底线。 |
5. 真实案例
Story
Instagram 的 3 人撑起 3000 万用户传奇 (2012)
2012年,当 Instagram 被 Facebook 以 10 亿美元收购时,他们只有 13 名员工,其中核心后端开发仅有 3 人。而当时他们要支撑 3000 万用户的海量图片社交。 他们的秘诀不是什么复杂的黑科技,而是极致简单的数据库设计。 他们利用 PostgreSQL 的基础关系特性 (Users, Media, Likes),将每一层关系都拆解得极其清晰,并建立了高效的 ID 索引。这种"简单的设计"让系统在指数级增长时,依然保持了丝般顺滑。
Vibe 心法:复杂度是软件工程的万恶之源。好的数据库设计,应该简单到连刚入职的实习生都能一眼看懂。
6. 本章小结
- 🧩 拆分:不要把所有数据塞进一个格子里,把它们拆成独立的对象。
- 🌉 桥梁:多对多关系(如购物车、标签)一定要用 中间表。
- 🧹 干净:保持 Schema 清晰,你的代码逻辑自然就会清晰。