这是本节的多页打印视图。
.
返回本页常规视图.
应用场景与架构模式
pg_mooncake (v0.3.0) 四大核心生产架构模式:实时操作分析(HTAP)、时间分区大表冷热归档、零 ETL Postgres 湖仓一体以及 OpenTelemetry 日志分析。
无需引入 Kafka、Debezium 或外部独立数仓,pg_mooncake 即可在 PostgreSQL 内部直接支撑以下四种高价值分析架构模式:
业务继续写入主 OLTP 行存表,将慢速聚合报表查询路由至亚秒级同步的 USING mooncake 列存镜像表。
为按时间分区的 PostgreSQL 大表创建列存镜像,定期从行存中 DROP 旧分区释放昂贵磁盘空间,同时在列存中完整保留可随时查询的全部历史数据。
自动将 PostgreSQL 业务表导出为 S3 或 Cloudflare R2 上的标准 Apache Iceberg v2 表,并在 Postgres 内直接查询外部 Iceberg / Delta Lake 数据集。
在 PostgreSQL 中承载海量应用日志与链路追踪事件,基于对象存储 Parquet 文件实现低成本、高吞吐的向量化聚合查询。
1 - 实时操作分析 (HTAP)
保持现有 PostgreSQL OLTP 写入路径不变,通过将分析查询路由至 USING mooncake 实时列存镜像表,实现百倍级分析提速。
如果你正在 PostgreSQL 业务表上运行缓慢的分析查询(例如实时报表看板、面向客户的统计指标、多维聚合查询),pg_mooncake 提供了一条极简的加速路径:
- 保持业务应用写入路径 100% 不变。
- 使用
CALL mooncake.create_table(dst, src) 创建实时列存镜像表。
- 将分析型
SELECT 查询路由至列存镜像表(往往无需修改 SQL 语句本身)。
核心优势
- 支持在线增删列:源行存表上的
ALTER TABLE ... ADD COLUMN 与 DROP COLUMN 操作会自动同步至列存镜像表。
- 高效支持高频
UPDATE 与 DELETE:moonlink 结合内存 Apache Arrow 写缓冲区与 Roaring Bitmap 删除向量,处理高频更新与删除的性能远超传统 Parquet / Iceberg 写入方案。
- 零 ETL 架构(Zero-ETL):无需部署运维 Debezium + Kafka 集群,直接复用 PostgreSQL 内置逻辑复制。
- 强事务一致性:分析查询在扫描前会自动同步等待当前 PostgreSQL WAL LSN 屏障,确保始终读取到最新已提交的一致性视图。
标准模式:写入行存表,查询列存镜像表
-- 1. 现有的 OLTP 业务表(承载事务写入与主键点查)
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
order_status TEXT NOT NULL DEFAULT 'pending',
order_date TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 2. 创建实时列存镜像表(专用于分析查询)
CALL mooncake.create_table('orders_analytics', 'orders');
-- 3. 写入操作:始终针对原始行存表执行
INSERT INTO orders (customer_id, total_amount) VALUES (123, 99.99);
UPDATE orders SET total_amount = 149.99, order_status = 'shipped' WHERE order_id = 1;
-- 4. 分析读取操作:查询列存镜像表
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(total_amount) AS lifetime_value
FROM orders_analytics
WHERE order_date >= CURRENT_DATE
GROUP BY customer_id;
显式查询路由说明
需手动指定查询目标表
pg_mooncake 不会自动拦截针对 orders 的查询并将其改写到 orders_analytics。请根据查询负载类型显式选择目标表:
-- 事务增删改与单行索引点查 -> 访问原始行存表 (orders)
INSERT INTO orders (customer_id, total_amount) VALUES (123, 99.99);
UPDATE orders SET order_status = 'shipped' WHERE order_id = 1;
DELETE FROM orders WHERE order_id = 1;
SELECT * FROM orders WHERE order_id = 1;
-- 大规模分析扫描与聚合计算 -> 访问列存镜像表 (orders_analytics)
SELECT customer_id, SUM(total_amount) FROM orders_analytics GROUP BY customer_id;
SELECT COUNT(*) FROM orders_analytics WHERE order_date >= CURRENT_DATE;
SELECT * FROM orders_analytics ORDER BY total_amount DESC LIMIT 10;
这种显式读写分离让你能够完全掌控数据库性能:事务操作在拥有 B-Tree 索引的行存堆表上保持亚毫秒级响应,而大吞吐分析扫描则充分发挥 DuckDB 2.0 在列存上的向量化执行优势。
2 - PostgreSQL 大表分区冷热归档
利用 pg_mooncake 将按时间分区的 PostgreSQL 大表归档为 Parquet 与 Apache Iceberg 格式,直接删除旧行存分区释放磁盘,同时在列存镜像中完整保留全部历史数据。
pg_mooncake 的列存镜像表将数据写为高压缩比的 Parquet 文件(保存在本地磁盘或 AWS S3 / Cloudflare R2 对象存储上),使你能够在 PostgreSQL 行存中仅保留近期热分区,同时在列存镜像中以极低成本持久化保留完整的历史数据。
架构设计模式
分区冷热归档模式遵循以下三条核心原则:
- 为按时间或范围分区的 PostgreSQL 表创建列存镜像(
CALL mooncake.create_table)。
- 定期从行存中
DROP 或 TRUNCATE 过期旧分区,立即释放昂贵的本地 NVMe / SSD 空间,避免 VACUUM FULL 锁表开销。
- 在列存镜像中完整保留全部历史数据——
pg_mooncake 会有意忽略针对分区源表子分区的 DROP TABLE 与 TRUNCATE 事件,因此被删除分区的历史行依然完整保留在列存镜像中并可随时查询!
完整实操示例
下面以一张按范围分区的 transactions 交易流水表为例演示完整归档流程。
第 1 步:创建分区行存表
CREATE TABLE transactions (
id INT,
amount DECIMAL(10, 2),
user_id INT,
created_at TIMESTAMP,
PRIMARY KEY (id)
) PARTITION BY RANGE (id);
第 2 步:创建实时列存镜像表
CALL mooncake.create_table('transactions_archive', 'transactions');
第 3 步:创建第一个子分区并写入数据
-- 创建承载 ID 0..50 的第一个子分区
CREATE TABLE transactions_p1 PARTITION OF transactions
FOR VALUES FROM (0) TO (50);
-- 插入示例交易数据
INSERT INTO transactions_p1 VALUES
(1, 99.99, 101, '2024-01-01 10:00:00'),
(2, 150.50, 102, '2024-01-01 11:00:00'),
(3, 75.25, 103, '2024-01-01 12:00:00');
第 4 步:从行存中直接删除历史旧分区
当 transactions_p1 变为历史冷数据、需要回收 PostgreSQL 本地磁盘空间时:
-- 直接从行存中删除旧子分区
DROP TABLE transactions_p1;
第 5 步:在列存镜像中查询历史数据
尽管 transactions_p1 已从行存中删除,但它的全部历史记录依然完好地保存在 transactions_archive 中:
SELECT * FROM transactions_archive ORDER BY id;
第 6 步:继续挂载新分区并写入增量数据
-- 为近期新数据创建第二个子分区
CREATE TABLE transactions_p2 PARTITION OF transactions
FOR VALUES FROM (50) TO (100);
-- 注意:如果分区表没有主键,在向新建子分区执行 UPDATE/DELETE 前请先设置:
-- ALTER TABLE transactions_p2 REPLICA IDENTITY FULL;
INSERT INTO transactions_p2 VALUES
(51, 200.00, 104, '2024-01-02 10:00:00'),
(52, 89.99, 105, '2024-01-02 11:00:00'),
(53, 125.75, 106, '2024-01-02 12:00:00');
-- 查询列存镜像表,将同时返回全部 6 条历史与最新数据
SELECT * FROM transactions_archive ORDER BY id;
最佳实践
- 切勿向已删除分区的键范围重复写入冲突数据:在删除或清空某个旧行存分区后,请勿重新创建相同范围的分区并写入重复主键,因为已删除分区的历史行仍保留在列存镜像中。
- 使用声明式分区表管理归档生命周期:
pg_mooncake 专门针对分区子表的 DROP / TRUNCATE 做了归档保留处理。
- 配合 S3 / R2 对象存储实现无限容量归档:在创建镜像前设置
mooncake.default_bucket = 's3://...',即可将所有历史 Parquet 文件与 Iceberg 元数据直接存储在廉价的云端对象存储上。详见 Iceberg 与对象存储配置。
3 - PostgreSQL 湖仓一体 (The Postgres Lakehouse)
在 PostgreSQL 中构建双向 Apache Iceberg v2 湖仓一体架构:无需 Kafka 或 Debezium 即可将业务表实时导出至 S3/R2,并在 SQL 中直接关联外部湖仓表。
**湖仓一体(Lakehouse)**架构将数据以开放表格式(Apache Iceberg 或 Delta Lake)统一存储在对象存储上,使多个计算引擎——PostgreSQL、DuckDB、Apache Spark、Snowflake、Trino 以及 Python(Polars / PyIceberg)——能够共享同一份数据,而无需反复拷贝。
pg_mooncake 让 PostgreSQL 在双向数据流中都成为湖仓架构的一等公民:
- Postgres $\rightarrow$ Iceberg 数据湖(零 ETL 导出):通过
moonlink 将在线 PostgreSQL 业务表实时复制为 AWS S3、Cloudflare R2 或本地存储上的标准 Apache Iceberg v2 表。
- 数据湖 $\rightarrow$ Postgres(库内直接联邦查询):通过
pg_duckdb 在 PostgreSQL 内直接扫描外部 Parquet、Apache Iceberg 与 Delta Lake 表,并与在线业务表实时 JOIN。
1. 将 Postgres 业务表零 ETL 导出为 Apache Iceberg v2
在传统架构中,将 PostgreSQL 数据同步到 Apache Iceberg 需要部署 Debezium、Kafka、Schema Registry 以及 Spark/Flink 消费作业。使用 pg_mooncake,只需三条 SQL 命令:
-- 1. 配置 S3 或 Cloudflare R2 存储桶凭据
INSERT INTO duckdb.secrets (name, type, key_id, secret, region, scope)
VALUES ('lakehouse_s3', 'S3', '<ACCESS_KEY>', '<SECRET_KEY>', 'us-east-1', 's3://company-lakehouse');
SET mooncake.default_bucket = 's3://company-lakehouse/iceberg';
-- 2. 将在线业务表镜像为基于 Iceberg 的列存表
CALL mooncake.create_table('customers_iceberg', 'customers');
-- 3. 刷写生成供外部引擎读取的 Iceberg v2 元数据快照
CALL mooncake.create_snapshot('customers_iceberg');
通过 SELECT * FROM mooncake.list_tables(); 查看生成的仓库路径后,Spark、Snowflake、Trino 或 PyIceberg 即可直接读取 <iceberg_warehouse_location>/metadata/v*.metadata.json。
2. 在 PostgreSQL 内直接关联查询外部湖仓表
与此同时,你的 PostgreSQL 应用也可以直接查询由外部 Spark 或机器学习流水线写入 S3 的 Iceberg 表,并与本地实时行存表直接关联:
SELECT duckdb.install_extension('iceberg');
SELECT
c.customer_id,
c.email,
ml.churn_probability
FROM customers c
JOIN duckdb.iceberg_scan('s3://company-lakehouse/ml_features/churn_scores/metadata/v3.metadata.json') AS ml(
customer_id BIGINT,
churn_probability DOUBLE PRECISION
) ON c.customer_id = ml.customer_id
WHERE ml.churn_probability > 0.80;
4 - 日志分析与 OpenTelemetry
结合 pg_mooncake 列存镜像与对象存储 Parquet 压缩,在 PostgreSQL 中以极低成本存储并分析海量应用日志与 OpenTelemetry 链路数据。
在传统架构中,人们很少使用 PostgreSQL 承载大规模日志分析,因为行存堆表与索引的磁盘占用增长极快,且对数千万条日志执行全表聚合扫描非常缓慢。
借助 pg_mooncake,你可以将按时间分区的 PostgreSQL 行存写入与基于对象存储的列存镜像结合起来:
- 极低存储成本:历史日志以高压缩比的列式 Parquet 格式保存在 S3、Cloudflare R2 或本地磁盘上。
- 架构极简:只需 PostgreSQL + 对象存储,无需额外部署运维 Elasticsearch 或 ClickHouse 集群。
- 高速向量化检索:利用 DuckDB 2.0 的时间戳分段跳过(Segment Elimination)与向量化扫描,每秒可扫描数百万条日志记录。
推荐实现方案
使用按天/按小时分区的行存表承接实时日志写入,并创建 USING mooncake 列存镜像表执行聚合检索:
-- 1. 创建按时间分区的日志写入表
CREATE TABLE application_logs (
log_id BIGSERIAL,
timestamp TIMESTAMPTZ NOT NULL,
level TEXT NOT NULL,
service TEXT NOT NULL,
message TEXT,
trace_id TEXT,
span_id TEXT,
attributes JSONB,
PRIMARY KEY (log_id, timestamp)
) PARTITION BY RANGE (timestamp);
CREATE TABLE application_logs_today PARTITION OF application_logs
FOR VALUES FROM ('2026-11-05') TO ('2026-11-06');
-- 2. 创建用于快速聚合检索的列存镜像表
CALL mooncake.create_table('application_logs_analytics', 'application_logs');
-- 3. 将日志写入行存表
INSERT INTO application_logs (timestamp, level, service, message, trace_id, span_id, attributes)
VALUES (
NOW(),
'ERROR',
'api-service',
'Database connection timeout',
'trace-123',
'span-456',
'{"user_id": 1001, "region": "us-east-1"}'
);
-- 4. 在列存镜像表上执行高速聚合分析
SELECT
service,
level,
COUNT(*) AS error_count
FROM application_logs_analytics
WHERE timestamp >= NOW() - INTERVAL '1 hour'
AND level = 'ERROR'
GROUP BY service, level
ORDER BY error_count DESC;
当每日日志分区超过热保留窗口(例如 24 或 48 小时后),可按照大表分区冷热归档模式直接从行存中删除旧分区(DROP TABLE application_logs_2026_11_01;),而完整的历史日志依然保留在 application_logs_analytics 中供长期回溯分析。