5 月的 Prairie Postgres Meetup 在芝加哥创新中心迎来了首场活动,Elizabeth Christensen(Snowflake)分享了主题演讲 pg_lake: Unifying transactional and analytical data with Postgres。核心命题很直接——能不能让同一套 Postgres 既跑在线事务,又跑分析查询,而不必维护两套独立系统?
这个话题之所以值得关注,是因为"事务库 + 分析库"的双栈架构几乎是每个中等规模团队的标配:Postgres 承载业务写入,Snowflake/BigQuery 负责报表和聚合。双栈意味着数据同步、Schema 对齐、延迟容忍等一系列运维负担。pg_lake 的方向是把这些负担收进 Postgres 生态内部。
事务与分析分栈的现实痛点
典型的数据架构是这样的:
- 业务系统写入 Postgres(OLTP)
- ETL 定期把数据搬到 Snowflake/Redshift(OLAP)
- 分析师在 OLAP 平台上跑聚合查询
问题出在"搬"这一步:
- 延迟:ETL 通常是小时级甚至天级调度,业务团队想看"过去 5 分钟的转化率"就很难。
- Schema 漂移:业务表改了字段,ETL 没跟上,分析结果就静默出错。
- 双份运维成本:两个平台各自的权限、监控、容灾。
pg_lake 想做的事情,本质上是在 Postgres 内部打通一条通往外部分析存储的管道,让事务查询和分析查询可以在同一个 SQL 接口下完成。
pg_lake 的设计意图
从演讲标题可以推断,pg_lake 的定位是 Postgres 的一个扩展(extension),它让 Postgres 能够直接查询外部对象存储(如 S3 上的 Parquet 文件)或分析型数据库中的数据,而不需要先把数据导入 Postgres 表。这样做的好处:
- 事务数据留在 Postgres 本地表,保证 ACID。
- 分析数据留在成本更低的对象存储或列式引擎中,通过 pg_lake 以 FDW(Foreign Data Wrapper)风格就地查询。
- 两种数据可以在一条 SQL 里 JOIN。
这和 Postgres 已有的 FDW 机制(postgres_fdw、parquet_fdw 等)思路相近,但 pg_lake 可能进一步优化了列式数据的读取路径和查询下推。
在 Postgres 中实践事务/分析一体化:可落地的方案
pg_lake 尚在演进中,但今天你就可以用 Postgres 自身能力搭建一个"轻量 HTAP"原型。以下是一个可复制的实践方案——用 postgres_fdw 把分析库的表映射到事务库,实现跨库 JOIN 查询。
步骤 1:准备两个 Postgres 实例
假设你在同一台机器上跑两个实例(事务端口 5432,分析端口 5433):
# 初始化分析实例的数据目录
initdb -D /tmp/pg_analytics_data
# 启动分析实例,端口 5433
pg_ctl -D /tmp/pg_analytics_data -o "-p 5433" -l /tmp/pg_analytics_log start
# 在分析实例上创建列存风格的宽表(用 citus 或原生 Postgres 均可)
psql -p 5433 -c "
CREATE DATABASE analytics;
"
psql -p 5433 -d analytics -c "
CREATE TABLE order_events (
event_id BIGINT PRIMARY KEY,
order_id BIGINT,
event_type TEXT,
amount NUMERIC(12,2),
occurred_at TIMESTAMPTZ
);
INSERT INTO order_events SELECT
generate_series(1, 100000),
(random() * 10000)::INT,
CASE WHEN random() < 0.6 THEN 'purchase'
WHEN random() < 0.8 THEN 'refund'
ELSE 'cancel' END,
(random() * 500)::NUMERIC(12,2),
now() - (random() * interval '30 days');
"
步骤 2:在事务库上挂载 FDW
-- 连接到事务实例(端口 5432)
psql -d myappdb
-- 安装 postgres_fdw 扩展
CREATE EXTENSION postgres_fdw;
-- 创建外部服务器,指向分析实例
CREATE SERVER analytics_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', port '5433', dbname 'analytics');
-- 创建用户映射(用当前连接用户)
CREATE USER MAPPING FOR current_user
SERVER analytics_server
OPTIONS (user '你的系统用户名');
-- 导入分析库的表
IMPORT FOREIGN SCHEMA public
LIMIT TO (order_events)
FROM SERVER analytics_server
INTO public;
-- 验证:外部表已可见
\d order_events
步骤 3:跨库 JOIN——事务表 + 分析表一条 SQL 出结果
假设事务库里有 orders 表,分析库有 order_events 表,你想查"每笔订单的退款总额":
-- 事务库本地表
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
customer TEXT,
status TEXT
);
-- 插入一些示例数据
INSERT INTO orders SELECT
generate_series(1, 10000),
'customer_' || (random() * 100)::INT,
CASE WHEN random() < 0.9 THEN 'active' ELSE 'closed' END;
-- 跨库 JOIN:事务 orders + 分析 order_events
SELECT
o.id AS order_id,
o.customer,
COALESCE(SUM(e.amount), 0) AS refund_total,
COUNT(e.event_id) AS event_count
FROM orders o
LEFT JOIN order_events e ON e.order_id = o.id AND e.event_type = 'refund'
GROUP BY o.id, o.customer
ORDER BY refund_total DESC
LIMIT 20;
这条查询在事务库上执行,order_events 的数据通过 FDW 从分析实例实时拉取。你不需要做 ETL,不需要等调度窗口,数据延迟等于网络延迟。
注意事项
- FDW 查询不总是高效:大表全表扫描会逐行拉取,务必在远程表上建合适索引,或在
IMPORT FOREIGN SCHEMA时用OPTIONS (use_remote_estimate 'true')让优化器参考远端统计信息。 - 事务库和分析库的资源隔离很重要:分析查询可能吃大量内存和 CPU,生产环境中建议把分析实例放在独立机器上,或用连接池限制并发。
- 上面的示例用两个 Postgres 实例模拟,真实场景中分析端可以是 Citus 集群、Parquet 文件(通过
parquet_fdw)甚至 Snowflake(如果 pg_lake 成熟后)。
从双栈到单栈:取舍清单
| 维度 | 双栈(Postgres + Snowflake) | 单栈(Postgres + pg_lake/FDW) |
|---|---|---|
| 查询性能(大规模聚合) | Snowflake 列存 + 弹性集群,优势明显 | Postgres 行存为主,超亿行聚合需 Citus 或外部列存 |
| 数据新鲜度 | 受 ETL 调度限制 | FDW 实时读取,秒级 |
| 运维复杂度 | 两套平台、两套权限 | 一套 Postgres 生态,FDW 配置相对轻 |
| 成本 | Snowflake 按查询量计费 | 对象存储 + Postgres 实例,通常更可控 |
| 生态成熟度 | 十年工业级验证 | pg_lake 刚起步,FDW 方案成熟但性能有边界 |
务实建议:
- 如果你的分析数据量在千万行以内、聚合查询并发不高,FDW 方案已经够用——今天就能落地。
- 如果分析负载重(TB 级、高并发聚合),保留独立分析引擎更稳妥,但可以用 pg_lake 做实时补充查询,替代部分 ETL。
- 关注 pg_lake 的后续发布,它的价值在于把 FDW 的"手动接线"变成标准化的、针对列存优化的内置通道。
6 月 4-5 日的 PG DATA 和 7 月 15 日的下一次 Meetup 会是跟进这个方向的好机会。统一事务与分析不会一步到位,但 Postgres 生态正在一步步缩小这个间隙。