PostgreSQL

PostgreSQL: pg_mooncake

dewstream 2025. 12. 24. 08:00
728x90

※ PostgreSQL: pg_mooncake.

 

안녕하세요. 듀스트림입니다.

 

크리스마스 이브입니다. 미리 크리스마스🎄

 

오늘 포스팅에서는 최근 분석 엔진으로 주목받고 있는 DuckDB를 PostgreSQL 환경에서 네이티브하게 활용할 수 있도록 지원하는 Extension인 pg_mooncake에 대한 내용입니다.


1. pg_mooncake ?

pg_mooncake는 PostgreSQL 테이블을 Iceberg 기반 컬럼 지향 스토리지로 미러링하여, Sub-second 수준의 최신성(freshness)을 유지하면서 고속 분석 쿼리를 가능하게 하는PostgreSQL Extension입니다.

 

pg_mooncake의 특징

  1. 컬럼스토어 미러링 구조
    • pg_mooncake는 PostgreSQL 테이블을 Iceberg 기반 컬럼스토어로 실시간/준실시간 미러링하여 빠른 분석 쿼리를 가능하게 하는 Extension입니다. (row-store → column-store mirror → analytic engine)
  2. real-time ingestion: moonlink
    • 미러링(데이터 복제/동기화)은 Rust 기반 moonlink를 통해 처리됩니다.
    • 공식 문서에서 "real-time ingestion powered by moonlink for streaming and batched INSERT/UPDATE/DELETE"로 소개되어 있으며, pg_mooncake가 이 데이터를 Iceberg에 반영하도록 설계되어 있습니다.
      • pg_mooncake = 컨트롤/메타 레이어
      • moonlink = 실제 데이터 파이프라인 엔진
  3. PostgreSQL logical replication 필요 (wal_level = logical)
    • pg_mooncake 설정에는 PostgreSQL에서 logical replication을 활성화하고 pg_mooncake가 WAL(Logical Decoding)을 처리하도록 구성해야 합니다.
  4. 컬럼스토어는 Apache Iceberg
    • 미러링된 데이터는 Apache Iceberg 포맷으로 저장되며, 이는 다른 엔진에서도 직접 접근 가능한 "open format" 컬럼스토어입니다.
    • PostgreSQL 전용 analytics mirror가 아니라, PostgreSQL → open analytics lakehouse 브리지입니다. (Spark, Trino, Presto, Flink에서도 접근 가능)
 

mooncake – managed + real-time Iceberg.

mooncake – managed Iceberg with streaming ingest for modern teams.

www.mooncake.dev

 

GitHub - Mooncake-Labs/pg_mooncake: Real-time analytics on Postgres tables

Real-time analytics on Postgres tables. Contribute to Mooncake-Labs/pg_mooncake development by creating an account on GitHub.

github.com


2. pg_mooncake 설치

▸ 패키지 설치

# config-manager 사용을 위한 플러그인 설치
dnf install -y dnf-plugins-core

dnf config-manager --set-enabled powertools

# 메타데이터 갱신
dnf makecache --refresh

# 개발 도구 패키지 설치
dnf groupinstall -y "Development Tools"
dnf install -y libcurl-devel openssl-devel zlib-devel cmake ninja-build lz4 lz4-devel
dnf install -y clang llvm llvm-devel

 

▸ Rust 설치

# 반드시 PG OS User로 변경 후 설치
su - postgres
cd ~

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 옵션은 디폴트로 Enter

 

→ 환경변수 설정

# For sh/bash/zsh/ash/dash/pdksh
. "$HOME/.cargo/env"

 

▸ cargo-pgrx 설치

cargo install --locked cargo-pgrx

 

▸ pg_mooncakde 설치

# PG OS User로 진행
cd ~

git clone --recurse-submodules https://github.com/Mooncake-Labs/pg_mooncake.git

cd pg_mooncake

git submodule update --init --recursive

cargo pgrx init --pg17=$(which pg_config) # Replace with your Postgres version

make PG_VERSION=pg17 pg_duckdb # Skip if pg_duckdb is already installed

make install PG_VERSION=pg17

 

▸ postgresql.conf 설정

# postgresql.conf
wal_level = logical
shared_preload_libraries = 'pg_duckdb,pg_mooncake'
duckdb.allow_community_extensions = true

 

▸ 익스텐션 생성

-- psql
CREATE EXTENSION pg_duckdb CASCADE;
CREATE EXTENSION pg_mooncake CASCADE;

3. 테스트

❗테스트 환경은 CPU: 8Core, Mem: 16GB, OS: Rocky8.10, PG: 17.7 입니다.

 

▸ 테스트 테이블 생성

CREATE TABLE trades(id bigint PRIMARY KEY, symbol text, time timestamp, price real);

 

▸ Parquet 기반 columnar 파일로 미러링

CALL mooncake.create_table('trades_iceberg', 'trades');

 

→ 함수 수행 시, Replication slot이 생성되고 logical replication이 시작됩니다.

SELECT slot_name, plugin, slot_type, active, restart_lsn, confirmed_flush_lsn, database, temporary FROM pg_replication_slots ORDER BY slot_name;

 

▸ mooncake 테이블 목록 확인

SELECT * FROM mooncake.list_tables();

 

▸ 테스트 데이터 삽입

-- 1억 건 삽입
INSERT INTO trades(id, symbol, time, price)
SELECT g AS id,
       CASE (g % 4)
           WHEN 1 THEN 'AMD'
           WHEN 2 THEN 'AMZN'
           WHEN 3 THEN 'AAPL'
           WHEN 0 THEN 'AMZN'
       END AS symbol,
       NOW() + (g || ' seconds')::interval AS time,
       (100 + (random() * 200))::real AS price
FROM generate_series(1, 100000000) AS g;

VACUUM ANALYZE trades;

 

▸ heap 테이블(row-oriented) 풀 스캔 테스트

EXPLAIN (ANALYZE, BUFFERS)
SELECT avg(price) FROM trades WHERE symbol = 'AMZN';

 

heap 테이블(row-oriented) Index 생성 후 테스트

-- 인덱스 생성
CREATE INDEX CONCURRENTLY trades_symbol_price_idx ON trades (symbol, price);
EXPLAIN (ANALYZE, BUFFERS)
SELECT avg(price) FROM trades WHERE symbol = 'AMZN';

→ 병렬 풀 스캔이랑 비슷하네요...

 

▸ Iceberg(column-oriented) 테스트

EXPLAIN (ANALYZE, BUFFERS)
SELECT avg(price) FROM trades_iceberg WHERE symbol = 'AMZN';

→ DuckDB 사용으로 대충 5000배 이상 빠르게 나옵니다.

 

+ mooncake 테이블 삭제 방법

-- 일반 테이블처럼 드랍 테이블로 삭제하면 됩니다.
-- 관련 파일도 알아서 삭제됩니다.
DROP TABLE public.trades_iceberg;

+ 테스트하다 문제 생겼을 때, DuckDB 인스턴스 초기화 방법

-- DuckDB Extension 로딩 과정에서 발생한 보안 서명 검증 실패 오류
-- Extension의 서명이 없거나 유효하지 않으며, unsigned extension 로딩이 설정상 금지되어 있어서 로드할 수 없다
ERROR:  (PGDuckDB/CreatePlan) IO Error: Extension "/data/pg_data/pg_duckdb/extensions/v1.4.1/linux_amd64/mooncake.duckdb_extension" could not be loaded because its signature is either missing or invalid and unsigned extensions are disabled by configuration (allow_unsigned_extensions)
-- DuckDB 내부 리소스를 강제로 정리(recycle)
CALL duckdb.recycle_ddb();

-- DuckDB에서 서명되지 않은 네이티브 확장을 실행할 수 있게 설정
SET duckdb.allow_unsigned_extensions = true;

→ DuckDB 입장에서 pg_mooncake는 서명되지 않은 비공식 확장 기능 (현재까지는 공식 확장 후보)


아래는 2주간 운영 및 테스트를 통해 확인한 pg_mooncake의 동작 특성과 이를 운영 환경에 적용할 때 고려해야 할 구조적 한계를 정리한 내용입니다.

 

pg_mooncake는 PostgreSQL 테이블을 WAL 기반 Logical Decoding 방식으로 수집하여, Iceberg 기반 컬럼스토어로 미러링하는 구조를 가지고 있습니다.

이로 인해 실시간에 가까운 분석 쿼리 성능을 제공할 수 있으나, 실제 운영 관점에서는 다음과 같은 특징 및 제약 사항이 확인되었습니다.

우선, 미러링 설정 이전에 존재하던 데이터는 컬럼스토어 테이블에 자동 반영되지 않습니다.

pg_mooncake는 CDC(변경 데이터 캡처) 기반 구조이기 때문에, 미러링 설정 이후 발생한 INSERT / UPDATE / DELETE 변경 사항만 컬럼스토어에 반영됩니다.

따라서 기존 데이터를 분석 대상으로 활용하기 위해서는 별도의 초기 적재 작업(INSERT … SELECT 등)이 필요하며, 이 과정은 운영 부하와 데이터 정합성 측면에서 추가적인 검토가 요구됩니다.

또한, WAL을 Iceberg 포맷으로 변환하는 moonlink 프로세스는 PostgreSQL 백그라운드 프로세스와는 별도의 사용자 프로세스로 동작하며, 단일 CPU 코어를 지속적으로 점유하고 데이터 변경량이 많을 경우 상당한 CPU/메모리(초기 적재 등 변경량이 많은 구간에서는 moonlink 코어, 메모리 거의 100% 사용)를 사용하는 경향이 확인되었습니다. 이는 분석용 미러링이라 하더라도, 운영 DB 서버 자원에 직접적인 영향을 줄 수 있음을 의미합니다.

구조적으로 pg_mooncake는 완전한 실시간 동기화 시스템이 아닙니다.

WAL 로그를 기반으로 비동기 변환 및 적재가 이루어지기 때문에, 트랜잭션 처리량이 급증하거나 일시적인 병목이 발생할 경우 컬럼스토어 반영 지연(latency) 이 발생할 수 있습니다.

특히 대량 트랜잭션 환경에서는 분석 결과가 운영 데이터와 미세하게 시차를 가질 수 있습니다.

운영 안정성 측면에서는 트랜잭션 롤백, 비정상 종료, replication slot 관련 이슈 발생 시 미러링 정합성 문제가 발생할 가능성도 확인되었습니다.

replication slot 자체는 재생성될 수 있으나, 특정 구간의 WAL 처리 실패 시 컬럼스토어 데이터가 불완전해질 수 있으며, 이 경우 전체 테이블 재적재가 필요할 수 있습니다.

pg_mooncake는 내부적으로 DuckDB 엔진을 활용하는 구조를 가지고 있으며, DuckDB의 signed extension 정책과 운영 환경의 보안 정책이 충돌할 가능성도 고려해야 합니다.

특히 보안이 엄격한 운영 환경에서는 extension 로딩 정책 자체가 도입 장애 요소가 될 수 있습니다.

추가적으로, pg_mooncake 적용 시 스토리지 사용량 증가에 대한 고려도 필요합니다.

원본 PostgreSQL 테이블 데이터 크기가 약 12GB인 환경에서, Iceberg 기반 컬럼스토어를 구성한 이후 다음과 같은 추가 스토리지 사용이 확인되었습니다.

  • 원본 PostgreSQL 데이터: 12GB
  • Iceberg 메타데이터: 약 5.2MB
  • Iceberg 데이터 + DuckDB 인덱스 파일: 약 9.1GB

즉, 원본 데이터 외에도 관리용 메타데이터와 컬럼스토어 데이터 및 인덱스가 별도로 생성되며, 원본 데이터 크기에 준하는 추가 스토리지가 사용되는 구조임을 확인할 수 있습니다.

이는 pg_mooncake가 단순한 데이터 복제가 아니라, 분석 성능을 위한 별도의 컬럼 지향 저장 구조와 인덱스를 유지하기 때문이며, 결과적으로 전체 스토리지 사용량은 운영 DB 단독 구성 대비 유의미하게 증가할 수 있습니다.

 

종합하면, pg_mooncake는 PostgreSQL 기반 데이터에 대해 매우 빠른 분석 쿼리를 제공할 수 있는 강력한 도구이지만, 운영 DB에 직접 설치하여 사용하는 경우에는 자원 사용량, 데이터 정합성, 장애 발생 시 복구 전략, 초기 데이터 적재 방식 등에 대한 충분한 사전 검증과 설계가 필수적입니다.

따라서 실서비스 운영 DB보다는 분석 전용 DB 또는 분리된 환경에서의 활용이 보다 현실적인 적용 시나리오로 판단됩니다.

 

+ 위와 같은 이유로 아래 정도 아키텍처 구성이 가능하다면, 사용해 볼 만할 것 같습니다. (어디까지나 아직은 실험적으로..)


다들 즐거운 크리스마스 보내세요~!

 

오늘은 여기까지~

 

728x90

'PostgreSQL' 카테고리의 다른 글

PostgreSQL: PgBouncer  (0) 2025.12.17
PostgreSQL: Patroni Cluster 사용 시 파라미터 변경 방법  (0) 2025.12.12
PostgreSQL: SELECT 효율 분석  (0) 2025.11.21
PostgreSQL: DISTINCT ON  (0) 2025.11.19
PostgreSQL: 스트리밍 복제 지연  (0) 2025.11.12