PostgreSQL 提供了一个坚实的基础核心,但扩展才是它从可靠的关系型数据库转变为更加多功能工具的关键。扩展生态系统是 Postgres 相比 MySQL、SQL Server 甚至 DynamoDB 或 Firestore 等托管数据库的最大竞争优势之一。您可以添加时间序列功能、支持30种语言的全文搜索、地理空间查询、列式存储和实时分析 — 所有这些都不需要离开 Postgres。
但通过 PGXN、GitHub 和供应商仓库可用的扩展超过1,000个,知道哪些真正重要就成功了一半。本指南介绍了2026年后端工程师和数据库管理员应该了解的10个扩展,包含实际配置示例、性能考虑因素,以及对每个扩展何时适用(何时不适用)的客观评估。
1. pg_stat_statements:每个人都应该启用的扩展
如果您在生产环境中运行 Postgres 且尚未启用 pg_stat_statements,请停止阅读并立即启用它。它会跟踪数据库运行的每个 SQL 语句的执行统计信息 — 总时间、调用次数、返回行数、块 I/O 等。
设置
# 在 postgresql.conf 中
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
# 然后重启 Postgres 并创建扩展
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
查找最慢的查询
SELECT
query,
calls,
round(total_exec_time::numeric, 2) AS total_ms,
round(mean_exec_time::numeric, 2) AS avg_ms,
rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
这个单一查询为我节省的调试时间比任何 APM 工具都多。total_exec_time 列告诉您数据库在哪里花费了最多的累积时间 — 一个运行2毫秒但每天执行500,000次的查询会出现在这里,而这正是您想要优化的查询类型。
需要关注的关键指标
- mean_exec_time vs. stddev_exec_time: 高标准差意味着性能不一致 — 可能是由于表膨胀、锁争用或参数相关的计划更改。
- shared_blks_hit vs. shared_blks_read: 比率告诉您每个查询的缓冲区缓存命中率。如果
shared_blks_read相对于shared_blks_hit较高,则该查询正在访问磁盘。 - rows / calls: 每次执行的平均行数。如果查询平均返回50,000行,您可能需要分页或更具体的 WHERE 子句。
在 Postgres 17 中,pg_stat_statements 增加了 toplevel 列,用于区分顶级查询和在函数或过程中执行的查询。这对于大量使用 PL/pgSQL 的团队来说非常重要——现在您可以准确查看哪些函数调用是昂贵的,而不需要猜测。
2. TimescaleDB:无需独立数据库的时间序列数据库
TimescaleDB 将 Postgres 转换为时间序列数据库。它按时间间隔将数据分区为”块”,并添加了时间感知的查询优化、压缩和连续聚合。
何时使用
如果您正在存储指标、物联网传感器数据、金融行情数据、事件日志或任何以时间戳为主要轴的追加密集型工作负载,在考虑使用 InfluxDB、QuestDB 或 ClickHouse 之前,TimescaleDB 值得评估。
设置和基本用法
CREATE EXTENSION IF NOT EXISTS timescaledb;
-- 将常规表转换为超表
CREATE TABLE sensor_readings (
time TIMESTAMPTZ NOT NULL,
sensor_id INTEGER NOT NULL,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION
);
SELECT create_hypertable('sensor_readings', by_range('time'));
在幕后,TimescaleDB 将 sensor_readings 分区为块(默认:7天间隔)。仅对 time 进行过滤的查询只会扫描相关的块,这比在包含数十亿行的单表上进行全表扫描要快得多。
压缩
TimescaleDB 的原生压缩是其最强大的功能之一。在典型的物联网工作负载中,可以实现 10-20 倍的压缩比:
ALTER TABLE sensor_readings SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'sensor_id',
timescaledb.compress_orderby = 'time DESC'
);
-- 自动压缩超过 7 天的块
SELECT add_compression_policy('sensor_readings', INTERVAL '7 days');
我合作的一个团队在 2025 年中期从 InfluxDB 2.x 迁移到 TimescaleDB 2.14。他们的主要动机是运营简单性——他们已经为应用数据运行了 Postgres,添加 TimescaleDB 意味着可以少管理一个数据库。对于他们的工作负载(每秒 50,000 次插入,90 天窗口内的聚合查询时间不到一秒),查询性能相当。由于更好的压缩,存储成本降低了 40%。
连续聚合
CREATE MATERIALIZED VIEW hourly_temperatures
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
sensor_id,
AVG(temperature) AS avg_temp,
MAX(temperature) AS max_temp,
MIN(temperature) AS min_temp
FROM sensor_readings
GROUP BY bucket, sensor_id;
-- 每小时自动刷新
SELECT add_continuous_aggregate_policy('hourly_temperatures',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);
连续聚合是增量维护的物化视图。与必须完全刷新的标准 Postgres 物化视图不同,TimescaleDB 只处理自上次刷新以来的新数据。这使得它们对于需要数月数据内亚秒级响应时间的仪表板非常实用。
3. pgvector: Postgres 中的向量搜索
AI/ML 的热潮使向量数据库成为热门类别,但对于大多数应用,pgvector 消除了对 Pinecone 或 Weaviate 等专用向量存储的需求。
设置
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
embedding vector(1536) -- OpenAI text-embedding-3-small 维度
);
-- 为快速近似最近邻搜索创建 HNSW 索引
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
查询
-- 找出与查询向量最相似的 10 个文档
SELECT id, title, 1 - (embedding <=> $1) AS similarity
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;
从 pgvector 0.8.0 版(2026年1月发布)开始,HNSW 索引构建时间比 0.6 版提高了约 2 倍,该扩展支持量化(二进制和标量)以减少内存使用。对于不超过 1000 万个向量的数据集,pgvector 在标准 recall@10 基准测试中的表现与专用向量数据库相差在 10-15% 以内——而且您获得了在单个事务中 alongside 关系数据运行向量搜索的巨大优势。
pgvector 的局限性
如果您需要索引 1 亿多个向量且 p99 延迟低于 10 毫秒,专用向量数据库仍然具有优势。此外,当筛选条件非常严格时,pgvector 的筛选(WHERE 子句 + 向量相似度)可能会很慢,因为 HNSW 索引本身不支持预筛选。解决方法是使用部分索引或适当探测数量的 ivfflat 索引类型。
4. PostGIS: 地理空间领域的黄金标准
PostGIS 已成为关系数据库中地理空间查询的事实标准,已有 20 多年的历史。如果您的应用存储位置、边界、路线或任何几何数据,PostGIS 几乎肯定是正确选择。
CREATE EXTENSION IF NOT EXISTS postgis;
-- 找出距离某点 2 公里内的所有餐厅
SELECT name, ST_Distance(
location::geography,
ST_SetSRID(ST_MakePoint(-73.9857, 40.7484), 4326)::geography
) AS distance_meters
FROM restaurants
WHERE ST_DWithin(
location::geography,
ST_SetSRID(ST_MakePoint(-73.9857, 40.7484), 4326)::geography,
2000
)
ORDER BY distance_meters;
PostGIS 3.5(截至2026年初的当前稳定版)为更多空间连接添加了并行支持,并改进了与 GEOS 3.13 的集成,以实现更快的多边形操作。如果您是从 PostGIS 3.2 或更早版本升级,复杂 ST_Intersection 和 ST_Union 操作的性能提升是显著的 — 基准测试显示在多边形数据集上速度提高了 30-50%。
5. pg_partman: 自动表分区
Postgres 中的原生分区(自 v10 起可用)功能强大,但需要手动分区管理。pg_partman 自动化分区的创建、保留和维护。
CREATE EXTENSION IF NOT EXISTS pg_partman;
-- 创建分区表
CREATE TABLE events (
id BIGSERIAL,
created_at TIMESTAMPTZ NOT NULL,
event_type TEXT,
payload JSONB
) PARTITION BY RANGE (created_at);
-- 让 pg_partman 管理它
SELECT partman.create_parent(
p_parent_table => 'public.events',
p_control => 'created_at',
p_interval => 'monthly',
p_premake => 3
);
p_premake => 3 参数告诉 pg_partman 提前 3 个月创建分区。后台工作进程负责创建新分区,并根据保留策略选择性地删除旧分区。
保留策略
-- 只保留 12 个月的数据
UPDATE partman.part_config
SET retention = '12 months',
retention_keep_table = false
WHERE parent_table = 'public.events';
对于高容量表(1亿+ 行),使用 pg_partman 进行分区不是可选的 — 这是必需的。没有它,VACUUM 操作会变得极其缓慢,索引维护膨胀,查询规划性能下降。一个拥有 20 亿行数据和月度分区的表,查询只会触及相关月份的分区(通常为 1.5-2 亿行),这对于顺序扫描和索引查找来说有着天壤之别。
6. pg_cron: 数据库内作业调度
与其在应用服务器上管理 cron 作业或使用外部调度器,pg_cron 直接在 Postgres 内部运行计划任务。
CREATE EXTENSION IF NOT EXISTS pg_cron;
-- 每晚 UTC 时间 3 点清理一个表
SELECT cron.schedule('nightly-vacuum-events', '0 3 * * *',
$$VACUUM ANALYZE events$$
);
-- 每 15 分钟刷新物化视图
SELECT cron.schedule('refresh-dashboard-mv', '*/15 * * * *',
$$REFRESH MATERIALIZED VIEW CONCURRENTLY dashboard_summary$$
);
-- 每小时删除旧会话
SELECT cron.schedule('cleanup-sessions', '0 * * * *',
$$DELETE FROM sessions WHERE expires_at < NOW() - INTERVAL '24 hours'$$
);
其优势在于简单性:调度信息存在于数据库中,在数据库中执行,您可以使用 SELECT * FROM cron.job_run_details ORDER BY start_time DESC; 监控作业历史。无需外部依赖。
一个注意事项:pg_cron 默认以数据库超级用户身份运行作业(或作业中指定的用户)。长时间运行的作业可能会占用连接并可能阻塞其他操作。对于重型 ETL 工作,像 Airflow 或 Dagster 这样的外部调度器仍然是更好的选择。
7. pgaudit: 合规级审计日志
如果您在医疗保健(HIPAA)、金融(SOX、PCI-DSS)或政府(FedRAMP)领域运营,您需要审计日志来显示谁在何时执行了什么 SQL。pgaudit 提供会话和对象级审计日志。
# 在 postgresql.conf 中
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'write, ddl'
pgaudit.log_relation = on
pgaudit.log_parameter = on
此配置记录所有写操作(INSERT、UPDATE、DELETE)和 DDL 语句(CREATE、ALTER、DROP),包括表名和查询参数。日志发送到标准 Postgres 日志目标,您可以通过 Fluentd、Vector 或 Datadog Agent 将其发送到您的 SIEM。
一个示例审计日志条目如下:
AUDIT: SESSION,1,1,WRITE,INSERT,,public.users,
"INSERT INTO users (email, name) VALUES ($1, $2)",
<john@example.com,John Doe>
对于对象级审计(记录对特定敏感表的访问),使用对象审计模式:
-- 审计对 payments 表的所有访问
SET pgaudit.role = 'auditor';
GRANT SELECT, INSERT, UPDATE, DELETE ON payments TO auditor;
现在,每次访问 payments 表的查询都会被记录,无论哪个用户执行。这是大多数合规团队想要的模式。
8. pg_repack: 在线表重组
随着 UPDATE 和 DELETE 操作留下死元组,Postgres 表会随时间积累膨胀。VACUUM 回收空间,但它不能重新组织表的物理布局或将空间回收给操作系统(只有 VACUUM FULL 可以做到,并且它会独占锁定表)。
pg_repack 在线重建表和索引,无需独占锁定:
-- 安装
CREATE EXTENSION IF NOT EXISTS pg_repack;
-- 重组一个膨胀的表(从命令行运行)
pg_repack -d mydb -t orders --no-superuser-check
-- 仅重组索引
pg_repack -d mydb -t orders --only-indexes
在底层,pg_repack 创建表的新副本,通过触发器重放更改,然后在短暂锁定中交换新旧表。即使对于包含数亿行的表,总锁定时间通常也低于 1 秒。
何时需要它
使用以下查询检查表膨胀:
SELECT
schemaname || '.' || tablename AS table_name,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size,
pg_size_pretty(
pg_total_relation_size(schemaname || '.' || tablename) -
pg_relation_size(schemaname || '.' || tablename)
) AS index_size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC
LIMIT 10;
如果一个表明显大于其包含的实际数据(您可以使用 pgstattuple 来估算),那么 pg_repack 是要使用的工具。根据我的经验,在频繁更新的表上每月运行 pg_repack 可以控制膨胀并保持一致的查询性能。
9. HypoPG:在不创建索引的情况下测试索引
在拥有 500 万行的表上创建索引需要时间并消耗 I/O。HypoPG 让您可以创建仅存在于查询规划器中的假设索引 — 不会实际构建索引,但 EXPLAIN 在规划查询时会使用它们。
CREATE EXTENSION IF NOT EXISTS hypopg;
-- 创建假设索引
SELECT * FROM hypopg_create_index(
'CREATE INDEX ON orders (customer_id, created_at DESC)'
);
-- 检查规划器是否会使用它
EXPLAIN SELECT * FROM orders
WHERE customer_id = 42
ORDER BY created_at DESC
LIMIT 10;
-- 清理假设索引
SELECT hypopg_reset();
这对于生产数据库的索引规划非常有价值。您可以先验证规划器是否会实际使用该索引,然后再提交,而不是直接创建索引(在大表上可能需要 30 分钟并消耗大量 I/O)。我曾见过团队创建了昂贵的索引,但规划器忽略了它们,因为选择性不够高 — HypoPG 可以避免这种浪费。
10. Citus:分布式 PostgreSQL
Citus 通过将表分片到多个节点,将 PostgreSQL 转变为分布式数据库。它现在是完全开源的(自微软收购该公司并于 2022 年在 AGPLv3 下发布),并且作为扩展提供,而不是分支。
CREATE EXTENSION IF NOT EXISTS citus;
-- 添加工作节点
SELECT citus_set_coordinator_host('coordinator.internal', 5432);
SELECT * FROM citus_add_node('worker1.internal', 5432);
SELECT * FROM citus_add_node('worker2.internal', 5432);
-- 分发表
SELECT create_distributed_table('orders', 'tenant_id');
-- 查询会自动分发
SELECT tenant_id, COUNT(*), SUM(amount)
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY tenant_id;
多租户 SaaS 用例
Citus 在多租户应用程序中表现出色,您可以通过 tenant_id 进行分片。对 tenant_id 进行过滤的查询会被路由到单个分片,这意味着随着租户的增加,性能会线性扩展 — 您只需添加更多工作节点即可。
我建议过的一个 SaaS 分析平台在最大租户的数据超过 500GB 时,从单个 Postgres 实例迁移到了 Citus。在使用 Citus 之前,该租户的仪表板查询需要 8-12 秒。在通过 tenant_id 分布到 4 个工作节点后,相同的查询只需 1-3 秒完成。总迁移工作量:约 3 周,大部分时间花在了调整连接分布式表和非分布式表的查询上。
何时不应使用 Citus
如果您的负载没有自然的分布键,Citus 会引入复杂性而不会带来相应的收益。跨分片查询(必须访问所有分片的查询)比在单个经过良好调优的实例上执行相同查询更慢。对于没有明确租户或分区键的分析工作负载,应考虑使用列式存储扩展或专用的 OLAP 数据库。
honorable 提及
还有一些其他值得了解的扩展,即使它们没有进入前十名:
- pgvector + pg_embedding: 如果 pgvector 的 HNSW 索引不适合您的内存预算,Neon 提供的
pg_embedding提供基于 DiskANN 的索引,能更优雅地溢出到磁盘。 - pg_stat_kcache: 通过操作系统级别的指标(CPU 时间、文件系统读取/写入)扩展
pg_stat_statements。需要在 Linux 上使用perf子系统。 - pgsodium: 使用 libsodium 进行列级加密。用于在无需更改应用层的情况下加密静态 PII(个人身份信息)。
- pglogical: 比内置逻辑复制更灵活的逻辑复制。支持选择性表复制、不同 Postgres 主要版本之间的复制以及双向复制。
- pg_hint_plan: 允许您使用 SQL 提示强制执行特定查询计划。当优化器做出错误决策且您无法重构查询时的最后手段工具。
安装和兼容性说明
并非所有扩展在所有托管 Postgres 提供商上都能使用。以下是截至 2026 年初主要平台的快速兼容性矩阵:
| 扩展 | AWS RDS | Google Cloud SQL | Azure Flexible | Neon | Supabase |
|---|---|---|---|---|---|
| pg_stat_statements | 是 | 是 | 是 | 是 | 是 |
| TimescaleDB | 否(使用 Timescale Cloud) | 否 | 否 | 否 | 是 |
| pgvector | 是 | 是 | 是 | 是 | 是 |
| PostGIS | 是 | 是 | 是 | 是 | 是 |
| pg_partman | 是 | 否 | 是 | 否 | 是 |
| pg_cron | 是 | 否 | 是 | 否 | 是 |
| pgaudit | 是 | 是(作为 pgAudit) | 是 | 否 | 否 |
| pg_repack | 否 | 否 | 否 | 否 | 否 |
| HypoPG | 否 | 否 | 否 | 是 | 是 |
| Citus | 否 | 否 | 是(原生) | 否 | 否 |
如果您自行托管 Postgres(或使用 Crunchy Data 或 Percona 等提供商),所有这些扩展都可用。托管提供商的限制是某些团队选择自行托管 Postgres 的主要原因 — 扩展可用性是一个真正的限制。
性能和资源考虑
扩展并非免费。每个通过 shared_preload_libraries 加载的扩展都会消耗共享内存,并增加 Postgres 启动过程的开销。以下是一些指导原则:
- pg_stat_statements 增加的开销可以忽略不计(CPU 使用率低于 1%),应该始终启用。
- TimescaleDB 为块管理增加内存开销。在高负载实例上,计划额外分配 128-256MB 的共享内存。
- pgvector HNSW 索引 是内存驻留的。一个具有 1536 维度的 1M 向量索引大约消耗 6-8GB 的 RAM。相应地调整您的实例大小。
- pgaudit 启用详细日志记录会显著增加 WAL 体积。如果您在高负载的 OLTP 数据库上记录所有 SELECT 语句,预计日志量会增加 3-5 倍。
- Citus 为分布式查询增加网络开销。将工作节点放置在同一可用区内以最小化延迟。
总结
Postgres 扩展生态系统是选择 Postgres 作为主数据库的最有力论据之一。与其运行五个专业数据库(关系型 + 时序 + 向量 + 地理空间 + 分析),您通常可以使用一个带有合适扩展的 Postgres 实例来满足需求。
也就是说,”Postgres 能做一切”并不等同于”Postgres 应该做一切”。扩展会增加复杂性,每个扩展都是另一个需要升级和维护的依赖项。务实的方法是先从 pg_stat_statements 开始(每个人都需要它),根据具体需求添加扩展,只有在扩展方法遇到明确的性能或操作限制时,才考虑专用系统。
本文涵盖的扩展——pg_stat_statements、TimescaleDB、pgvector、PostGIS、pg_partman、pg_cron、pgaudit、pg_repack、HypoPG 和 Citus——代表了生态系统中经过实战检验且广泛采用的最常用工具。掌握这十个扩展,你将能够处理大多数后端工程挑战,而无需转向其他数据库。
