数据持久化与数据库
7.11 数据库性能优化
Tip
同样的功能,有的人写出来是"秒开",有的人写出来是"转圈圈"。区别不在于电脑好坏,而在于你是否懂得"吝啬"。
1. 为什么要学这个?
你的电商网站刚上线时,因只有 10 个商品,怎么点都飞快。 今天上午,运营导入了 10 万个商品。 你再次打开首页,加载圈圈足足转了 5 秒钟才出来。
也就是这 5 秒钟,一半的用户以为网站挂了,直接关掉页面走了。 学会这章的技巧,哪怕数据量再翻 100 倍,你的网站依然能像闪电一样快。
2. 核心概念:让程序变快的三招
第一招:给数据编目录 (索引)
场景:你要在 10 万个用户里找一个叫 "张三" 的人。
- 慢做法 (没索引):你得拿着名单,从第一个人名开始,一行一行往下看。如果运气不好,张三在最后一行,你就得看 10 万次。
- 快做法 (有索引):你手里有一本"姓氏目录"。直接翻到 "Z",瞬间就找到了。
怎么做? 告诉 Prisma,你会经常按 "email" 查人。它就会自动给 email 造一个目录。
model User {
email String
@@index([email]) // 👈 这里!给 email 加个目录
}
第二招:只拿你需要的 (查询瘦身)
场景:你去超市买瓶水。
- 慢做法 (
findMany不加参数):售货员把超市里所有的商品(大米、电视、冰箱...)都打包塞进你车里,虽然你其实只要一瓶水。 - 快做法 (
select):你明确告诉售货员:"我只要水"。
代码对比:
// ❌ 慢:把用户的头像、简介、密码全拿回来了,浪费流量
const users = await prisma.user.findMany();
// ✅ 快:只取 id 和 name,轻装上阵
const users = await prisma.user.findMany({
select: { id: true, name: true }
});
第三招:别让快递员跑空趟 (解决 N+1)
场景:你要给 100 个客户送货。
- 笨办法 (N+1):回公司取第 1 个快件 -> 送到客户家 -> 空手回公司 -> 取第 2 个快件...
- 你得在路上跑 200 趟!
- 聪明办法 (
include):在公司一次性把 100 个快件装车 -> 出发一趟送完。
代码对比:
// ❌ 笨办法:在循环里查数据库
for (const user of users) {
// 每一轮循环,都要去数据库跑一趟,极慢!
await prisma.post.findMany(...)
}
// ✅ 聪明办法:一次打包带走
const users = await prisma.user.findMany({
include: { posts: true } // 👈 顺便把这人的文章也带回来
});
3. 避坑指南
| ❌ 错误做法 | ✅ 正确做法 | 💡 为什么 |
|---|---|---|
| 循环里查库 | 打包查询 | 这是新手最容易犯的错。记住:for 循环里永远不要写 await 数据库代码。 |
| 全都查出来 | 分页 (take) |
永远不要试图一次性把所有数据都展示给用户。如果表里有 100 万行,你的服务器内存会瞬间撑爆。 |
| 到处加索引 | 也就是查谁加谁 | 索引虽然好,但如果不查的字段也加索引,就像给书的每一页都贴标签,反而让书变厚了(写入变慢)。 |
4. 真实案例
Story
Stack Overflow 的 2 台服务器神话
全球最大的程序员问答网站 Stack Overflow,每天有几千万人在上面查问题。但你敢信吗?支撑这庞大流量的数据库服务器,竟然只有 2 台。 他们的秘诀不是买超贵的电脑,而是极其吝啬。 工程师会为了节省哪怕 1 毫秒的时间,反复修改查询代码,确保绝不拿多余的数据,绝不跑冤枉路。
Vibe 心法:电脑虽然很快,但也不禁造。写代码时要像守财奴一样,对每一个字节、每一次查询都精打细算。
5. 本章小结
- 查得慢:先看看是不是忘了加索引(目录)。
- 传得慢:是不是拿了太多不该拿的字段?
- 循环慢:是不是在循环里反复跑腿了?