← 返回资讯
林远舟
技术编辑
已审核

PostgreSQL 性能调优:从 30 秒到 300 毫秒的优化之路

我们有一个报表查询,高峰期要跑 30 秒。用户投诉后,我开始系统性地优化。最终降到 300 毫秒。

PostgreSQL 性能调优:从 30 秒到 300 毫秒的优化之路

PostgreSQL 性能调优:从 30 秒到 300 毫秒的优化之路

我们有一个报表查询,高峰期要跑 30 秒。用户投诉后,我开始系统性地优化。最终降到 300 毫秒。

第一步:找到慢查询

SQL
-- 开启慢查询日志
ALTER SYSTEM SET log_min_duration_statement = 1000;  -- 记录超过 1 秒的查询
SELECT pg_reload_conf();

-- 查看当前正在跑的查询
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;

第二步:EXPLAIN ANALYZE

SQL
EXPLAIN ANALYZE
SELECT o.*, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at > '2024-01-01'
AND o.status = 'completed'
ORDER BY o.created_at DESC
LIMIT 50;

关键指标:

第三步:加索引

SQL
-- 复合索引,匹配查询条件
CREATE INDEX idx_orders_status_created 
ON orders(status, created_at DESC);

-- 覆盖索引,避免回表
CREATE INDEX idx_orders_covered 
ON orders(status, created_at DESC) 
INCLUDE (total, user_id);

-- 部分索引,仅对活跃数据
CREATE INDEX idx_active_orders 
ON orders(created_at DESC) 
WHERE status IN ('pending', 'processing');

第四步:优化查询

SQL
-- 优化前:子查询 + NOT IN
SELECT * FROM orders
WHERE user_id NOT IN (
  SELECT id FROM blocked_users
);

-- 优化后:LEFT JOIN + IS NULL
SELECT o.* FROM orders o
LEFT JOIN blocked_users b ON o.user_id = b.id
WHERE b.id IS NULL;

-- 分页优化:用游标代替 OFFSET
-- 慢
SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 100000;

-- 快
SELECT * FROM orders 
WHERE id > 100000 
ORDER BY id LIMIT 20;

第五步:连接池

SQL
-- 查看当前连接数
SELECT count(*) FROM pg_stat_activity;

-- 合理配置连接池(应用层)
-- PgBouncer 或应用内连接池

最终效果

| 指标 | 优化前 | 优化后 |

|------|--------|--------|

| 查询时间 | 30s | 300ms |

| CPU 使用率 | 85% | 30% |

| 连接数 | 120 | 20 |

核心思路:找到慢查询 → 分析执行计划 → 加索引 → 改写 SQL → 配置优化。

334
11145 阅读
3 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年07月12日 11:21

林远舟

技术编辑

全栈工程师出身,做过 5 年技术社区运营。对 AI 编程工具、开发者生态有深入研究,喜欢用实测数据说话。

读者评论 3

M
创业者Mark 4天前
正在做相关方向,这篇文章给了我不少启发。
回复 点赞 (7)
老李 1周前
有个小问题想请教,文中提到的那个方案在大规模场景下性能怎么样?
回复 点赞 (5)
运营小陈 1周前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)