3 个悄无声息拖垮应用性能的 PostgreSQL 反模式

原文:https://dev.to/mindinu/3-postgresql-anti-patterns-that-are-silently-killing-your-apps-performance-1e5k (作者 @mindinu)

PostgreSQL 是地球上最强大的关系型数据库之一。开箱即用,它就能处理海量工作负载。但随着你的应用扩展,编写查询和设计 Schema 的方式比数据库引擎本身变得更加重要。

如果你的应用开始感觉迟钝,可能不是资源不足。你可能陷入了以下三个常见的 PostgreSQL 反模式之一。

下面是如何发现它们以及如何修复它们。

1. SELECT * 陷阱(以及它为何破坏内存)

当我们快速迭代时,很容易就忍不住直接写 SELECT * FROM users,然后让后端过滤掉不需要的字段。

为何是反模式:

PostgreSQL 必须从磁盘读取数据,加载到内存,然后通过网络发送给你的应用。如果你的 users 表有 30 列(包括笨重的 JSONB 大对象或大型 TEXT 字段),而你只需要 idemail,你就是在强迫数据库做 10 倍的 I/O 工作,毫无意义。

此外,SELECT * 会破坏仅索引扫描。如果你在 email 上有索引,像 SELECT email FROM users WHERE email = 'x' 这样的查询可以纯粹从索引中解析,甚至不需要访问主表。SELECT * 会迫使 Postgres 获取整行数据。

修复方法:

始终显式定义你的列,即使你在使用 ORM。

-- ❌ 错误
SELECT * FROM orders WHERE status = 'pending';

-- ✅ 正确
SELECT id, customer_id, total_amount FROM orders WHERE status = 'pending';


2. 过度索引(“随便加个索引”的谬误)

当查询变慢时,第一反应通常是:“我们就在那个列上加个索引吧!”

为何是反模式:

索引并非免费。每次你 INSERTUPDATEDELETE 一行时,PostgreSQL 必须更新主表以及与该表关联的每一个索引。如果你有一个写入密集型表(比如事件日志器或分析跟踪器)上有 10 个不同的索引,你的写入延迟会急剧上升。

此外,Postgres 查询规划器很聪明。如果一个索引选择性不高(例如,一个像 is_active 这样的布尔列,其中 95% 的用户都是活跃状态),Postgres 可能会完全忽略该索引,仍然执行顺序扫描。你正在为一个从未被使用的索引支付写入开销!

修复方法:

  1. 定期检查未使用的索引。Postgres 会为你追踪这个!你可以运行这个查询来找到数据库正在忽略的索引:
SELECT relname, indexrelname, idx_scan 
FROM pg_catalog.pg_stat_user_indexes 
WHERE idx_scan = 0;


  1. 删除未使用的索引,并为频繁按相同多列过滤的查询改用组合索引

3. ORM 的 N+1 查询灾难

如果你在使用 Prisma、TypeORM、Hibernate 或 Eloquent,你可能在不知不觉中写过这个确切的 bug。

为何是反模式:

N+1 问题发生在你的代码获取了一列记录,然后循环遍历该列表以获取每条记录的相关数据。

// ❌ N+1 灾难实例
const users = await db.users.findMany(); // 1 次查询
for (const user of users) {
  // 这为每一个用户都运行一个新查询!(N 次查询)
  const posts = await db.posts.find({ authorId: user.id }); 
}


如果你有 1,000 个用户,你刚刚为了本应是单次往返的事情,通过网络向数据库发送了 1,001 次请求。这是 API 延迟的首要原因。

修复方法:

使用 JOIN 或依赖你的 ORM 的预加载功能,在单个优化查询中获取所有数据。

// ✅ 正确:在单次往返中获取用户及其文章
const usersWithPosts = await db.users.findMany({
  include: { posts: true }
});


注意:在底层,这实际执行为一个 `LEFT JOIN` 或者确切的两次查询(一次查询用户,一次通过 `IN` 子句查询匹配这些用户 ID 的所有文章)。

总结

扩展数据库不仅仅是为你的云服务商增加更多 RAM 和 CPU。它关乎尊重网络边界、理解你的索引,并密切关注你的 ORM 实际生成的 SQL。

原文:https://dev.to/mindinu/3-postgresql-anti-patterns-that-are-silently-killing-your-apps-performance-1e5k (作者 @mindinu)

发布评论
全部评论(0)