Vibe Tutorial
数据持久化与数据库

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. 本章小结

  1. 🧩 拆分:不要把所有数据塞进一个格子里,把它们拆成独立的对象。
  2. 🌉 桥梁:多对多关系(如购物车、标签)一定要用 中间表
  3. 🧹 干净:保持 Schema 清晰,你的代码逻辑自然就会清晰。