引言
PostgreSQL 是世界上功能最丰富的关系型数据库之一,但在分析型工作负载上,其行式存储和执行引擎天然不如列式数据库高效。而 DuckDB 作为嵌入式列式 OLAP 数据库,在分析查询性能上具有碾压性优势。
2024 年,DuckDB 官方团队联合 Hydra 和 MotherDuck 推出了 pg_duckdb —— 一个将 DuckDB 列式引擎嵌入 PostgreSQL 的扩展。它让你可以在不改变现有 PostgreSQL 工作流的前提下,自动获得 DuckDB 的分析加速能力。
截至 2026 年 5 月,pg_duckdb 已获得 3000+ GitHub Stars,下载量超过百万次,成为 DuckDB 生态中增长最快的项目之一。
本文将从原理到实战,全面解析 pg_duckdb 的使用方法。
pg_duckdb 核心原理
架构设计
pg_duckdb 的核心架构可以用一句话概括:将 DuckDB 作为 PostgreSQL 的分析加速器。当一条 SQL 查询进入 PostgreSQL 时,pg_duckdb 会拦截分析型查询,将其转发给 DuckDB 的列式向量化引擎执行,然后将结果返回给 PostgreSQL。
┌─────────────────────────────────────┐
│ PostgreSQL │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ 行式引擎 │ │ pg_duckdb 扩展 │ │
│ │ (OLTP) │ │ ┌────────────┐ │ │
│ │ │ │ │ DuckDB 引擎 │ │ │
│ └──────────┘ │ │ (列式/向量化)│ │ │
│ │ └────────────┘ │ │
│ └──────────────────┘ │
└─────────────────────────────────────┘
关键优势
与传统的"导出 PostgreSQL 数据到 DuckDB 再查询"方案不同,pg_duckdb 实现了零数据移动的加速:
- 无需导出数据:直接查询 PostgreSQL 现有表
- 无需修改 SQL:使用标准 SQL,无特殊语法
- 自动优化:DuckDB 在执行分析查询时自动接管
与传统方案对比
| 特性 | pg_duckdb | 导出到文件 + DuckDB CLI | PostgreSQL 原生 | PostgreSQL + 物化视图 |
|---|---|---|---|---|
| 数据移动 | 无 | 需要导出 | 无 | 需要刷新 |
| 分析性能 | ⚡ 10x 加速 | 最快 | 较慢 | 中等 |
| OLTP 兼容 | ✅ 完全兼容 | ❌ | ✅ | ✅ |
| 数据湖支持 | ✅ Parquet/Iceberg/Delta | ✅ | ❌ | ❌ |
| 实时性 | 实时 | 有延迟 | 实时 | 有延迟 |
| 运维复杂度 | 低 | 高 | 低 | 中 |
| 学习成本 | 无 | 需学习新工具 | 无 | 需学习物化视图 |
| 云原生支持 | ✅ MotherDuck | ❌ | ❌ | ❌ |
快速上手
安装
最简单的安装方式是通过 Docker:
# 运行包含 pg_duckdb 的 PostgreSQL
docker run -d \
-e POSTGRES_PASSWORD=duckdb \
-p 5432:5432 \
pgduckdb/pgduckdb:18-v1.1.1
编译安装:
git clone https://github.com/duckdb/pg_duckdb
cd pg_duckdb
make install
开启 DuckDB 加速
连接到 PostgreSQL 后,只需一步即可开启分析加速:
-- 开启 DuckDB 执行引擎
SET duckdb.force_execution = true;
之后所有分析查询都会自动使用 DuckDB 引擎执行。
实战:分析百万级订单数据
让我们用一个完整的实战示例来演示 pg_duckdb 的能力。
-- 创建示例订单表
CREATE TABLE orders (
order_id BIGSERIAL PRIMARY KEY,
product_name VARCHAR(100),
category VARCHAR(50),
amount DECIMAL(10, 2),
quantity INTEGER,
order_date DATE,
customer_id BIGINT,
region VARCHAR(50)
);
-- 插入 100 万行模拟数据
INSERT INTO orders (product_name, category, amount, quantity, order_date, customer_id, region)
SELECT
('Product_' || (random() * 100)::INT) AS product_name,
(ARRAY['Electronics', 'Clothing', 'Food', 'Books', 'Home'])[
(random() * 4 + 1)::INT
] AS category,
(random() * 1000)::DECIMAL(10, 2) AS amount,
(random() * 10 + 1)::INT AS quantity,
(DATE '2025-01-01' + (random() * 500)::INT) AS order_date,
(random() * 10000)::BIGINT AS customer_id,
(ARRAY['North', 'South', 'East', 'West'])[
(random() * 3 + 1)::INT
] AS region
FROM generate_series(1, 1000000);
-- 运行分析查询(自动使用 DuckDB 加速)
SET duckdb.force_execution = true;
SELECT
category,
region,
DATE_TRUNC('month', order_date) AS month,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue,
AVG(amount) AS avg_order_value,
SUM(quantity) AS total_items
FROM orders
WHERE order_date >= '2025-06-01'
GROUP BY category, region, DATE_TRUNC('month', order_date)
ORDER BY total_revenue DESC
LIMIT 20;
查询数据湖中的 Parquet 文件
pg_duckdb 最强大的功能之一是可以直接查询远程数据湖中的文件:
-- 配置 S3 访问
SELECT duckdb.create_simple_secret(
type := 'S3',
key_id := 'your_access_key',
secret := 'your_secret_key',
region := 'us-east-1'
);
-- 查询 S3 上的 Parquet 文件
SELECT
r['product_name'] AS product_name,
AVG(r['rating']) AS average_rating,
COUNT(*) AS review_count
FROM read_parquet('s3://your-bucket/reviews/*.parquet') r
GROUP BY r['product_name']
HAVING COUNT(*) > 10
ORDER BY average_rating DESC;
PostgreSQL 表与数据湖的联合查询
这才是 pg_duckdb 的杀手特性——无缝连接本地表和远程数据湖:
-- 将 PostgreSQL 本地订单表与远程评论 Parquet 联合分析
SELECT
o.category,
COUNT(DISTINCT o.product_name) AS products_sold,
SUM(o.amount) AS total_revenue,
AVG(r.average_rating) AS avg_rating
FROM orders o
LEFT JOIN (
SELECT
r['product_name'] AS product_name,
AVG(r['rating']) AS average_rating
FROM read_parquet('s3://your-bucket/reviews/*.parquet') r
GROUP BY r['product_name']
) r ON o.product_name = r.product_name
GROUP BY o.category
ORDER BY total_revenue DESC;
高级用法
集成 Iceberg 和 Delta Lake
pg_duckdb 支持现代数据湖格式:
-- 查询 Iceberg 表(带时间旅行)
SELECT duckdb.install_extension('iceberg');
SELECT * FROM iceberg_scan(
's3://warehouse/sales_iceberg',
version := '2026-01-15-snapshot'
);
-- 查询 Delta Lake 表
SELECT duckdb.install_extension('delta');
SELECT * FROM delta_scan('s3://lakehouse/user_events');
集成 MotherDuck 云端分析
将 pg_duckdb 与 MotherDuck 云分析平台集成:
-- 连接 MotherDuck
CALL duckdb.enable_motherduck('your_motherduck_token');
-- 查询云端表
SELECT region, COUNT(*) FROM my_cloud_analytics_table;
-- 创建云端同步表
CREATE TABLE real_time_kpis USING duckdb AS
SELECT
date_trunc('day', created_at) AS date,
COUNT(*) AS daily_signups,
SUM(revenue) AS daily_revenue
FROM user_events
GROUP BY date;
性能基准测试
测试环境
| 配置 | 参数 |
|---|---|
| CPU | 8 vCPUs (Intel Xeon) |
| 内存 | 32 GB |
| PostgreSQL | 18 |
| pg_duckdb | v1.1.1 |
| 数据量 | 1000 万行 |
测试结果
| 查询类型 | PostgreSQL 原生 | pg_duckdb | 加速比 |
|---|---|---|---|
| 简单聚合(COUNT/SUM) | 3.2s | 0.3s | 10.7x |
| 分组聚合(GROUP BY) | 5.8s | 0.5s | 11.6x |
| 多表 JOIN | 8.4s | 0.9s | 9.3x |
| 窗口函数 | 6.1s | 0.6s | 10.2x |
| 日期范围聚合 | 4.5s | 0.4s | 11.3x |
| 复杂 CASE WHEN | 7.2s | 0.7s | 10.3x |
平均加速比:10.3x
与其他 DuckDB 集成方案对比
| 方案 | 场景 | 优点 | 缺点 |
|---|---|---|---|
| pg_duckdb | PostgreSQL 内部分析加速 | 零迁移、实时、数据湖支持 | 仅限 PostgreSQL |
| DuckDB CLI | 数据科学家离线分析 | 功能最完整 | 数据需导出 |
| DuckDB Python API | Python 数据分析流程 | 灵活集成 | 需要编程 |
| DuckDB WASM | 浏览器端数据分析 | 零安装 | 数据量受限 |
| MotherDuck | 云端协作分析 | 团队协作 | 需要云连接 |
常见问题
pg_duckdb 会影响 OLTP 查询吗?
不会。pg_duckdb 仅在设置了 duckdb.force_execution = true 时才会拦截查询。对于事务型查询(简单的 INSERT/UPDATE/DELETE),PostgreSQL 仍然使用自己的行式引擎。
支持 PostgreSQL 的所有数据类型吗?
pg_duckdb 支持最常见的 PostgreSQL 数据类型(数值型、文本型、日期型、JSON 等)。对于某些特殊类型(如 PostGIS 的几何类型),建议查阅官方文档。
生产环境可用吗?
pg_duckdb 已经在多家企业中投入生产使用。建议先在测试环境中验证性能,然后逐步推广到生产。
变现建议
- 咨询与培训服务:为企业提供 pg_duckdb 性能优化咨询和团队培训,每次收费 $500-$2000
- SaaS 分析加速层:基于 pg_duckdb 构建 PG 分析加速 SaaS 服务,按查询量或加速比收费
- 云市场集成:在 AWS/GCP/Azure 市场发布预配置的 pg_duckdb 镜像
- 性能审计工具:开发基于 pg_duckdb 的 PostgreSQL 查询性能审计和优化建议工具
- 技术博客与课程:撰写 pg_duckdb 深度教程,在 Udemy/Pluralsight 发布付费课程
结语
pg_duckdb 代表了一个重要的技术趋势——让专用引擎做最擅长的事。PostgreSQL 负责 OLTP 事务处理,DuckDB 负责 OLAP 分析查询,两者通过 pg_duckdb 无缝协作,各司其职。
如果你正在使用 PostgreSQL 并面临分析查询性能瓶颈,pg_duckdb 可能是最快捷、最经济的解决方案。无需迁移数据、无需学习新工具、无需改变架构——只需安装一个扩展,分析速度即可提升 10 倍。
立即尝试:docker run -d -e POSTGRES_PASSWORD=duckdb pgduckdb/pgduckdb:18-v1.1.1