上一篇文章介绍了 Mooncake-Labs 架构核心模块 moonlink, 上游实时数据流以读优化方式入湖(iceberg), 说是实时入湖, 实际上有一部分是在moonlink的缓存里.
同时介绍了duckdb与moonlink配合使用的例子
实际上pg_mooncake 也从0.1演进到了0.2, 包括通过本地表的逻辑订阅, 将数据映射为iceberg列存表(通过moonlink实时接入), 同时借助DuckDB作为计算引擎, 在PG内可以试试查询iceberg列存表.
怎么用呢? 来看看项目文档
https://docs.mooncake.dev/pg/intro/architecture
https://github.com/Mooncake-Labs/pg_mooncake
pg_mooncake是一个 Postgres 扩展,它在Iceberg中创建 Postgres 表的列存储镜像,从而实现亚秒级新鲜度的快速分析查询:
默认情况下,pg_mooncake 将 Moonlink 作为 Postgres worker process。这让您在单一且易于管理的Postgres中同时拥有读写iceberg的功能。
对于生产环境,我们建议:
这种架构实现了存储和计算的完全分离,使 pg_mooncake 完全无状态。我们常见的一种模式是将 pg_mooncake 作为单独的分析数据库运行,与事务型 Postgres(RDS、Supabase 等)并存。这通常如下所示:
┌────────────┐
│ │logical
│ │replication
│ ├──────────┐
│ Postgres │ │
│ OLTP │ ┌─────▼──────┐
└────────────┘ │┌──────────┐│
││ ││ ┌─────────┐
││ │├───│ │
┌────────────┐ ││ moonlink ││ │s3 │
│ │ │└──────────┘│ └─────────┘
│ │ └─────┬──────┘
│ │ │
│pg_mooncake │ │
│ ◄──────────┘
└────────────┘stateless
reads
对于新用户,我们建议使用 Docker 镜像快速上手:
docker run --name mooncake --rm -e POSTGRES_PASSWORD=password mooncakelabs/pg_mooncake
这将启动 Postgres 并预装 pg_mooncake。然后你可以使用psql和默认用户postgres连接它:
docker exec -it mooncake psql -U postgres
要构建 pg_mooncake,首先需安装Rust、pgrx和DuckDB 的构建工具。
然后,克隆存储库:
git clone --recurse-submodules https://github.com/Mooncake-Labs/pg_mooncake.git
支持Postgres 14-17 版本:
# Replace with your Postgres version
cargo pgrx init --pg17=$(which pg_config)
make install PG_VERSION=pg17
最后,在参数shared_preload_libraries中添加 pg_mooncake 并启用逻辑复制:
shared_preload_libraries = 'pg_mooncake'
wal_level = logical
如需完整演练,请参阅我们的 Dockerfile 。
首先,创建 pg_mooncake 扩展:
CREATE EXTENSION pg_mooncake;
接下来,创建一个常规的 Postgres 表 trades:
CREATE TABLE trades(
id bigint PRIMARY KEY,
symbol text,
time timestamp,
price real
);
然后,创建一个与 trades 表保持同步的列存储镜像表 trades_iceberg :
CALL mooncake.create_table('trades_iceberg', 'trades');
现在,往 trades 表插入一些数据:
INSERT INTO trades VALUES
(1, 'AMD', '2024-06-05 10:00:00', 119),
(2, 'AMZN', '2024-06-05 10:05:00', 207),
(3, 'AAPL', '2024-06-05 10:10:00', 203),
(4, 'AMZN', '2024-06-05 10:15:00', 210);
最后,查询trades_iceberg以查看它是否反映了最新的trades状态:
SELECT avg(price) FROM trades_iceberg WHERE symbol = 'AMZN';
下面介绍一些 pg_mooncake 的场景实践.
如果您在 Postgres 上的分析查询运行缓慢,pg_mooncake 为您提供一种简单的方法来加速这些查询:
这有一些好处,例如:
你只需要照常写入行存储,分析类可以从列存储读取。
-- Your existing OLTP table (for transactions)
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
order_date TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Create analytics replica (for analytics)
CALL mooncake.create_table('orders_analytics', 'orders');
-- Write operations: use the original table
INSERT INTO orders (customer_id, total_amount) VALUES (123, 99.99);
UPDATE orders SET total_amount = 149.99 WHERE order_id = 1;
-- Read operations: use the analytics table
SELECT customer_id, SUM(total_amount)
FROM orders_analytics
WHERE order_date >= CURRENT_DATE
GROUP BY customer_id;
重要提示:您需要手动将查询路由到适当的表(行存表 或 列存表)。
pg_mooncake 不会自动路由查询 - 您可以根据工作负载选择要查询的表:
-- Transactional queries → Original table
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;
-- Analytical queries → Analytics table
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;
这使您可以完全控制性能 - 事务操作在行存储上保持快速,而分析查询则利用列存储的性能优势。
这个场景是 湖仓.
Lakehouse 架构允许您将数据以开放表格式(例如iceberg)存储在对象存储中。其主要优势在于独立于供应商 —— 您可以使用任何您选择的工具(Snowflake、Spark、DuckDB 等)读取这些表。
传统上,管理和将数据写入 Lakehouse 需要专门的数据工程工具,如 Spark、EMR、Kafka 和 Flink。
pg_mooncake 简化了这一过程:仅使用 PostgreSQL 构建和管理您的数据湖,无需复杂的数据工程堆栈。
pg_mooncake 允许您写入完全受管理/优化的 Iceberg 表。
它取代了复杂的 Debezium + Kafka ,并且不再需要管理复杂的catalog等。
创建自动导出到 Iceberg 的列存储表:
-- Create columnstore table (automatically generates Iceberg)
CALL mooncake.create_table('orders_iceberg', 'orders');
-- Your data is now available as optimized Iceberg tables
SELECT * FROM mooncake.columnstore_tables
WHERE table_name = 'orders_iceberg';
schema(DDL)更改和数据修改将自动处理:
-- Schema changes are handled automatically
ALTER TABLE orders ADD COLUMN priority TEXT;
-- Iceberg table schema updates automatically
-- Updates and deletes use Iceberg v3 deletion vectors for efficiency
UPDATE orders SET priority = 'high' WHERE total_amount > 1000;
DELETE FROM orders WHERE order_date < '2023-01-01';
-- Iceberg state preserved with optimal performance
您的 Iceberg 表可立即从多个分析引擎进行查询:
-- Query from Postgres with pg_mooncake
SELECT * FROM orders_iceberg WHERE order_date >= '2024-01-01';
-- Query from DuckDB
SELECT * FROM iceberg_scan('s3://bucket/iceberg/orders_iceberg');
-- Query from Spark
SELECT customer_id, COUNT(*) as order_count
FROM iceberg.default.orders_iceberg
GROUP BY customer_id;
Mooncake 的列存储将数据以 Parquet 格式写入对象存储,提供经济高效、持久化的历史记录,而 Postgres 仅维护热分区。这种方法非常适合时间分区表, 需要保留历史数据并保持精简的OLTP数据库。
归档模式遵循以下关键原则:
这为您提供了两全其美的效果:对最新数据的快速操作查询和廉价、可查询的分析历史记录。
下面是使用INT范围作为基于时间的分区替代的完整示例。
步骤 1:创建分区行存储
CREATE TABLE transactions (
id INT PRIMARY KEY,
amount DECIMAL(10,2),
user_id INT,
created_at TIMESTAMP
) PARTITION BY RANGE (id);
步骤2:创建列存储
CALL mooncake.create_table('transactions_archive', 'transactions');
步骤3:添加分区并插入
-- Create first partition
CREATE TABLE transactions_p1 PARTITION OF transactions
FOR VALUES FROM (0) TO (50);
-- Insert sample data
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:存档旧数据
当您准备存档旧数据时:
-- Drop the old partition from rowstore
DROP TABLE transactions_p1;
步骤5:查询历史
-- Query the columnstore (history is retained)
SELECT * FROM transactions_archive;
步骤 6:继续新建
-- Add new partition for recent data
CREATE TABLE transactions_p2 PARTITION OF transactions
FOR VALUES FROM (50) TO (100);
-- Insert new data
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');
-- Query columnstore returns all historical data
SELECT * FROM transactions_archive; -- Returns all six rows
数据一致性
常见用法
pg_mooncake 为大容量日志分析和可观察性数据提供了 Postgres 原生解决方案,使其成为 ClickHouse 或专用日志分析平台的绝佳替代品。
它为“日志分析”场景提供了几个优势:
目前推荐的日志分析模式遵循标准的写入行存储、读取列存储的方法:
-- Create your log table
CREATE TABLE application_logs (
timestamp TIMESTAMP NOT NULL,
level TEXT NOT NULL,
service TEXT NOT NULL,
message TEXT,
trace_id TEXT,
span_id TEXT,
attributes JSONB
);
-- Create columnstore mirror for analytics
CALL mooncake.create_table('application_logs_analytics', 'application_logs');
-- Write logs to the rowstore table
INSERT INTO application_logs VALUES
(NOW(), 'ERROR', 'api-service', 'Database connection failed', 'trace-123', 'span-456', '{"user_id": 1001}');
-- Query logs from the analytics table
SELECT service, level, COUNT(*) as error_count
FROM application_logs_analytics
WHERE timestamp >= NOW() - INTERVAL '1 hour'
AND level = 'ERROR'
GROUP BY service, level;
提取日志后,您可以从行存储中存档旧分区,同时在列存储中保留完整的历史记录以供长期分析。
我们正在开发一种增强的体验,它将提供: