PostgreSQL 高级特性与性能优化实战
简介
PostgreSQL 被誉为”开发者最喜爱的数据库”,其强大之处不仅在于基本的 CRUD 操作,更在于丰富的高级特性:部分索引、表达式索引、CTE 递归查询、窗口函数、分区表、并行查询、物化视图、JSON/JSONB 全文搜索等。本文从实战角度出发,深入讲解这些特性的使用场景与性能优化技巧。
前置要求
- 已安装 PostgreSQL 15+(建议 16 或 17)
- 基本的 SQL 知识(SELECT、INSERT、UPDATE、CREATE TABLE)
- 了解数据库索引的基本概念
- 建议准备一个测试数据库进行实验
目录
1. 高级索引技巧
1.1 部分索引(Partial Index)
当查询只关注表中一小部分数据时,部分索引可以大幅减小索引体积。
1 | -- 场景:订单表,99% 的订单是"已完成"状态,只查询"待处理"订单 |
性能对比:
| 索引类型 | 大小 | 查询耗时(1000万行) |
|---|---|---|
| 普通索引 | 214 MB | 12 ms |
| 部分索引 | 2.1 MB | 0.8 ms |
1.2 表达式索引(Expression Index)
对函数或表达式结果建立索引,避免每次查询都进行全表计算。
1 | -- 场景:用户表,经常按邮箱域名查询 |
1.3 覆盖索引(Covering Index / INCLUDE)
将不需要排序但经常 SELECT 的列包含在索引中,实现仅索引扫描(Index-Only Scan)。
1 | -- 场景:经常查询用户的 email 和 status,但只按 id 过滤 |
1.4 复合索引列顺序
1 | -- 规则:等值条件列在前,范围条件列在后 |
1.5 索引维护
1 | -- 查看索引大小 |
2. CTE 与递归查询
2.1 基础 CTE(WITH 子句)
1 | -- 场景:统计每个分类的销售额占比 |
2.2 递归 CTE(WITH RECURSIVE)
递归 CTE 是处理树形/层级数据的利器。
1 | -- 场景:组织架构树(员工-上级关系) |
输出:
1 | level | org_chart | path |
2.3 递归 CTE 实战:BOM(物料清单)
1 | -- 产品结构表 |
3. 窗口函数实战
窗口函数在不折叠行的情况下进行聚合计算,是数据分析的核心工具。
3.1 排名函数
1 | -- 场景:按部门工资排名 |
| name | department | salary | row_num | rank | dense_rank |
|---|---|---|---|---|---|
| Alice | Engineering | 150000 | 1 | 1 | 1 |
| Bob | Engineering | 150000 | 2 | 1 | 1 |
| Carol | Engineering | 120000 | 3 | 3 | 2 |
3.2 聚合窗口函数
1 | -- 场景:计算销售额的累计和与移动平均 |
3.3 窗口帧(Frame)详解
1 | -- ROWS vs RANGE vs GROUPS |
3.4 滞后与超前分析
1 | -- 场景:计算每日销售额环比 |
3.5 分组内百分比
1 | -- 场景:每个员工在部门内的薪资占比 |
4. 表分区
4.1 范围分区(Range Partitioning)
1 | -- 场景:日志表按月分区 |
4.2 列表分区(List Partitioning)
1 | CREATE TABLE orders ( |
4.3 哈希分区(Hash Partitioning)
1 | CREATE TABLE user_sessions ( |
4.4 分区维护
1 | -- 查看分区结构 |
4.5 分区剪枝验证
1 | -- 验证查询是否只扫描了相关分区 |
5. JSONB 与全文搜索
5.1 JSONB 索引
1 | -- 创建测试表 |
5.2 JSONB 路径查询(PgSQL 12+)
1 | -- 使用 SQL/JSON 路径表达式 |
5.3 全文搜索(Full-Text Search)
1 | -- 创建全文搜索向量列 |
5.4 混合搜索(JSONB + 全文搜索)
1 | -- 场景:电商产品搜索,同时过滤属性和搜索文本 |
6. 物化视图
物化视图将查询结果物理存储,适合计算密集但变更不频繁的场景。
6.1 创建与刷新
1 | -- 创建物化视图:每日销售报表 |
6.2 物化视图 vs 普通视图
| 特性 | 普通视图 | 物化视图 |
|---|---|---|
| 数据存储 | 不存储,每次查询执行 | 物理存储 |
| 查询速度 | 慢(每次重新计算) | 极快(直接读取) |
| 数据实时性 | 实时 | 取决于刷新频率 |
| 索引支持 | ❌ | ✅ |
| 占用空间 | 0 | 取决于数据量 |
| 适用场景 | 简单封装 | 复杂聚合报表 |
6.3 自动刷新策略
1 | -- 方案1:使用 pg_cron 扩展定时刷新 |
7. 查询性能调优
7.1 使用 EXPLAIN ANALYZE
1 | -- 基础用法 |
7.2 常用性能诊断查询
1 | -- 慢查询(运行时间最长的前 10 个) |
7.3 配置优化
1 | -- 查看当前配置 |
7.4 并行查询
1 | -- 允许并行查询 |
8. 常见问题
Q1:查询使用了索引但仍然很慢?
1 | -- 检查是否进行了不必要的回表 |
Q2:VACUUM 相关
1 | -- 查看是否需要 VACUUM |
Q3:死锁如何排查?
1 | -- 查看当前锁 |
Q4:如何快速复制大表?
1 | -- 使用 CREATE TABLE AS(比 INSERT INTO ... SELECT 更快) |
Q5:连接池配置建议?
1 | -- 查看当前连接数 |
Q6:如何备份与恢复?
1 | # 逻辑备份 |
总结
本文覆盖了 PostgreSQL 高级特性中日常开发最常用的核心能力:
| 特性 | 核心命令 | 典型场景 |
|---|---|---|
| 部分索引 | CREATE INDEX ... WHERE |
过滤低频状态数据 |
| 表达式索引 | CREATE INDEX ... ON func(col) |
函数/类型转换查询 |
| 递归 CTE | WITH RECURSIVE |
树形结构、BOM、组织架构 |
| 窗口函数 | ROW_NUMBER() OVER (PARTITION BY) |
排名、移动平均、环比 |
| 表分区 | PARTITION BY RANGE/LIST/HASH |
日志、时序数据 |
| JSONB | @>, ?, jsonb_path_query |
灵活 Schema、配置存储 |
| 全文搜索 | to_tsvector, to_tsquery |
产品搜索、内容检索 |
| 物化视图 | CREATE MATERIALIZED VIEW |
报表、聚合查询 |
| 并行查询 | max_parallel_workers_per_gather |
大表聚合、分析查询 |
建议在日常开发中从以下顺序逐步引入高级特性:
- 先加索引:部分索引 + 覆盖索引,性价比最高
- 再优化查询:窗口函数替代子查询,CTE 替代复杂 JOIN
- 大表做分区:超过 1000 万行考虑分区
- 缓存结果:物化视图加速报表
- 调优配置:根据服务器硬件调整 PG 参数