跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

应用场景与架构模式

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 提供了一条极简的加速路径:

  1. 保持业务应用写入路径 100% 不变。
  2. 使用 CALL mooncake.create_table(dst, src) 创建实时列存镜像表。
  3. 将分析型 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 行存中仅保留近期热分区,同时在列存镜像中以极低成本持久化保留完整的历史数据。

架构设计模式

分区冷热归档模式遵循以下三条核心原则:

  1. 为按时间或范围分区的 PostgreSQL 表创建列存镜像(CALL mooncake.create_table)。
  2. 定期从行存中 DROP 或 TRUNCATE 过期旧分区,立即释放昂贵的本地 NVMe / SSD 空间,避免 VACUUM FULL 锁表开销。
  3. 在列存镜像中完整保留全部历史数据——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 在双向数据流中都成为湖仓架构的一等公民:

  1. Postgres $\rightarrow$ Iceberg 数据湖(零 ETL 导出):通过 moonlink 将在线 PostgreSQL 业务表实时复制为 AWS S3、Cloudflare R2 或本地存储上的标准 Apache Iceberg v2 表。
  2. 数据湖 $\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 中供长期回溯分析。