Language:Chinese VersionEnglish Version

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——代表了生态系统中经过实战检验且广泛采用的最常用工具。掌握这十个扩展,你将能够处理大多数后端工程挑战,而无需转向其他数据库。

By Michael Sun

Founder and Editor-in-Chief of NovVista. Software engineer with hands-on experience in cloud infrastructure, full-stack development, and DevOps. Writes about AI tools, developer workflows, server architecture, and the practical side of technology. Based in China.

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed