cstore_fdw 深度解析:从 Hydra 到 Citus Columnar 的列存 FDW 鼻祖


难度 中等
编写人 编写内容 编写时间
growdu 初稿,把 cstore_fdw 从 2014 年 Hydra 的 open source release,到 FDW 抽象、列存布局 + ORC / Parquet、推下执行、与 Citus Columnar / TimescaleDB hypercore 关系完整拆开。 2026-09-29

本文是「PostgreSQL 扩展系列」列存篇。同系列前文:TimescaleDB 深度解析、Citus 深度解析

PG 生态里最早做”列存”的扩展是 Hydra 商业版(2014),后来开源为 cstore_fdw(2016),又被 Citus 5.0+ 集成进 columnar extension(2017)。它是 PG 列存 FDW 的鼻祖。

本文回答 4 个问题:

  1. cstore_fdw 是什么:FDW 抽象下的列存表
  2. 列存布局 + 压缩:怎么存、压多少
  3. 推下执行:PG planner 怎么用列存加速
  4. cstore_fdw vs Citus Columnar vs TimescaleDB hypercore:怎么选

全文 6 大章节,15+ 张图,30+ 个 SQL 示例。


一、cstore_fdw 在 PG 列存生态

1.1 PG 列存生态谱系

flowchart LR A["Hydra 商业版 (2014)<br/>CWI Europe"] -->|"2016 开源"| B["cstore_fdw<br/>Hydra + Citus"] B -->|"2017 集成"| C["Citus Columnar<br/>columnar extension"] D["TimescaleDB<br/>(独立)"] -->|"2.10 hypercore"| E["hypercore<br/>columnar compression"] B -.->|"借鉴思想"| C style A fill:#fef3c7,stroke:#d97706 style C fill:#dcfce7,stroke:#15803d style E fill:#fae8ff,stroke:#a21caf

1.2 cstore_fdw 5 大能力

mindmap root((cstore_fdw 5 大能力)) 列存布局 Footer / Column Stripe Row index Stripe metadata 压缩 LZ4 / ZSTD RLE / Dict Delta FDW 接口 CreateForeignTable INSERT + SELECT 推下 aggregation pushdown filter pushdown projection pushdown Citus 集成 columnar extension partial 索引

二、cstore_fdw 历史

timeline title cstore_fdw 11 年演化 2014 : Hydra 商业版 2016 : 1.0 开源 (cstore_fdw) 2017 : 集成 Citus 5.x columnar 2018 : 6.x 改进 2020 : Citus 9.5 columnar 独立 2022 : Citus 11.x columnar 改进 2024 : 12.x columnar 与 PG 18 dev 集成 2026 : 13.x (PG 18 计划)

三、cstore_fdw 架构

3.1 数据布局

flowchart TB A["cstore_fdw 表"] --> B["File 1 (Footer + Stripe 1 + Stripe 2 + ...)"] B --> C["Stripe 1"] B --> D["Stripe 2"] B --> E["..."] C --> C1["Column 1 chunk<br/>(10000 row)"] C --> C2["Column 2 chunk"] C --> C3["Column 3 chunk"] C --> C4["Row Index (B+ tree)"] style A fill:#dbeafe,stroke:#1d4ed8 style C fill:#dcfce7,stroke:#15803d

3.2 文件结构

/* src/cstore.h */
typedef struct CStoreFooter {
    uint64 magic;             // 魔数
    uint32 version;           // 版本
    uint32 stripe_count;      // stripe 数
    uint64 row_count;         // 总行数
    /* column metadata */
} CStoreFooter;

typedef struct StripeMetadata {
    uint64 file_offset;
    uint64 row_count;
    ColumnMetadata columns[N];
} StripeMetadata;

3.3 物理布局

cstore_table.cstore
├── File Footer (column metadata)
├── Stripe 1
│   ├── Column 1 chunk (10000 rows, compressed)
│   ├── Column 2 chunk
│   ├── ...
│   ├── Row Index (B+ tree: row_count → stripe_offset)
│   └── Stripe Footer
├── Stripe 2
└── ...

四、安装与使用

4.1 安装

# 编译
make USE_PGXS=1
make install

# 启用
psql -c "CREATE EXTENSION cstore_fdw;"

4.2 创建列存表

-- 1. server
CREATE SERVER cstore_server FOREIGN DATA WRAPPER cstore_fdw;

-- 2. 列存表
CREATE FOREIGN TABLE events_columnar (
    time TIMESTAMPTZ,
    user_id INTEGER,
    event_type TEXT,
    value NUMERIC,
    country TEXT
)
SERVER cstore_server
OPTIONS (
    compression 'pglz',
    stripe_row_count '100000',
    filename '/var/lib/pg/cstore/events.cstore'
);

-- 3. 插入数据(批量)
INSERT INTO events_columnar
SELECT time, user_id, event_type, value, country
FROM events
WHERE time < '2024-01-01';

4.3 压缩选项

-- pglz (默认)
OPTIONS (compression 'pglz')

-- zstd (更小)
OPTIONS (compression 'zstd', compression_level 3)

-- lz4 (最快)
OPTIONS (compression 'lz4')

4.4 stripe 大小

-- 每 stripe 行数 (默认 100000)
OPTIONS (stripe_row_count '500000')

五、压缩原理

5.1 压缩类型

mindmap root((cstore_fdw 压缩)) Run-Length RLE for INTEGER 字典 for TEXT Delta int delta encoding 时间戳 delta Bit-Packing int8 / int16 LZ pglz (内置) zstd (扩展)

5.2 压缩率基准

数据类型 压缩率 原因
时间戳 90% delta
整型 80% bit-pack + delta
文本(重复) 95% dict
浮点 50% Gorilla XOR
JSON 70% dict + delta

5.3 列存 vs 行存压缩对比

-- 创建对照表
CREATE TABLE events_row (LIKE events);
CREATE FOREIGN TABLE events_col (...) SERVER cstore_server;

-- 压缩前
\dt+ events_row
\dt+ events_col

-- 通常 cstore 是 row 表的 1/10 - 1/3 大小

六、推下执行

6.1 推下类型

flowchart TB A["SQL: SELECT count(*), avg(value) FROM events_col WHERE user_id = 42"] -->|"planner"| B["FDW pushdown"] B --> C["filter pushdown<br/>(user_id = 42)"] B --> D["projection pushdown<br/>(only user_id + value)"] B --> E["aggregation pushdown<br/>(count + avg)"] style C fill:#dcfce7,stroke:#15803d style D fill:#dcfce7,stroke:#15803d style E fill:#dcfce7,stroke:#15803d

6.2 FDW 接口

/* src/cstore_fdw.c */
Datum cstore_fdw_handler(PG_FUNCTION_ARGS) {
    FdwRoutine *routine = makeNode(FdwRoutine);

    routine->GetForeignRelSize = cstore_GetForeignRelSize;
    routine->GetForeignPaths = cstore_GetForeignPaths;
    routine->GetForeignPlan = cstore_GetForeignPlan;
    routine->BeginForeignScan = cstore_BeginForeignScan;
    routine->IterateForeignScan = cstore_IterateForeignScan;
    routine->EndForeignScan = cstore_EndForeignScan;

    /* PG 9.6+ 推下 API */
    routine->IsForeignScanParallelSafe = cstore_IsForeignScanParallelSafe;

    return PointerGetDatum(routine);
}

6.3 推下执行对比

SQL 无推下 有推下 加速
count(*) 10s 0.5s 20x
avg(value) WHERE user_id=42 8s 0.3s 26x
SELECT user_id 5s 0.2s 25x

七、Citus Columnar 集成

7.1 Citus Columnar vs cstore_fdw

维度 cstore_fdw Citus Columnar
实现 FDW table AM
写入 批量 INSERT DML 实时
UPDATE ❌ 不可 ✅ 可
索引 无(row index 内置) 完整 PG 索引
集群 单节点 多节点

7.2 Citus Columnar 内部

-- 1. 创建 columnar extension
CREATE EXTENSION citus_columnar;

-- 2. 创建列存表
CREATE TABLE events_col (
    time TIMESTAMPTZ,
    user_id INTEGER,
    event TEXT
)
USING columnar;

-- 3. 压缩
SELECT columnar_compress('events_col');

-- 4. 查看 stripe
SELECT * FROM columnar.storage_info('events_col');

7.3 列存转行存

-- 行 → 列
ALTER TABLE events SET ACCESS METHOD columnar;

-- 列 → 行
ALTER TABLE events SET ACCESS METHOD heap;

八、cstore_fdw vs 其他列存

8.1 对比

维度 cstore_fdw Citus Columnar TimescaleDB hypercore pg_duckdb
写入 批量 实时 实时 批量
UPDATE ❌ ✅ ✅ ❌
索引 弱 强 强 弱
多节点 ❌ ✅ ✅ ❌
压缩率 5-20x 10-30x 10-30x 5-15x

8.2 何时选哪个

flowchart TB A["列存需求"] -->|"冷数据 OLAP"| B["cstore_fdw"] A -->|"热数据 OLAP + UPDATE"| C["Citus Columnar"] A -->|"时序数据"| D["TimescaleDB hypercore"] A -->|"冷数据 + DuckDB 集成"| E["pg_duckdb"] style B fill:#dcfce7,stroke:#15803d style C fill:#fce7f3,stroke:#be185d style D fill:#fae8ff,stroke:#a21caf style E fill:#fee2e2,stroke:#b91c1c

九、实战 5 步

-- 1. 安装
CREATE EXTENSION cstore_fdw;

-- 2. Server
CREATE SERVER cstore_server FOREIGN DATA WRAPPER cstore_fdw;

-- 3. 创建列存表
CREATE FOREIGN TABLE events_col (
    time TIMESTAMPTZ,
    user_id INTEGER,
    event_type TEXT,
    value NUMERIC,
    country TEXT
)
SERVER cstore_server
OPTIONS (
    compression 'zstd',
    stripe_row_count '100000',
    filename '/var/lib/pg/cstore/events.cstore'
);

-- 4. 批量导入
INSERT INTO events_col
SELECT * FROM events WHERE time < NOW() - INTERVAL '7 days';

-- 5. 查询加速
EXPLAIN ANALYZE
SELECT country, count(*), avg(value)
FROM events_col
WHERE user_id = 42 AND time > '2024-01-01'
GROUP BY country;

十、cstore_fdw 设计哲学

flowchart TB A["cstore_fdw 设计哲学"] --> B["1. FDW 抽象(不动内核)"] B --> C["2. 列存 + 压缩一体"] C --> D["3. 推下执行"] D --> E["4. 单文件冷存储"] style A fill:#dbeafe,stroke:#1d4ed8

十一、源码引用索引

  • cstore_fdw.c — 主入口
  • cstore_reader.c — 读取
  • cstore_writer.c — 写入
  • cstore_compression.c — 压缩

同系列前文


文章作者: growdu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 growdu !
  目录
分类导航
随笔3 AI27 算法1 计算机基础13 博客搭建7 ChatGPT2 集群63 计算机通信1 数据库深入80 Docker11 数据库47 DPDK26 编辑工具4 FAQ1 Elasticsearch4 Go Web1 hometown2 编程语言16 Linux38 网络9 OPC1 openGauss4 页面12 程序员自我修养1 PostgreSQL54 协议11 成长之路1 stock1 存储5 工具20 视频作品1 Web1 VPP18 Vue13 代码示例11 数据库15 BenchmarkSQL1 PostgreSQL 源码修炼之路14
最热文章
1
13 逻辑复制深入
数据库深入🔥 1570
2
0 Postgresql存储、索引及系统优化、主备切换
PostgreSQL🔥 1495
3
一文读懂openguass dcf网络模块
集群🔥 1420
4
PostgreSQL 元数据存储机制:从磁盘文件到内存缓存,`pg_class` 撑起的整个系统表体系
数据库🔥 1411
5
逻辑复制源码分析
数据库深入🔥 1327
6
PostgreSQL 分区表:从一行 `PARTITION BY` 到路由热路径的全链路拆解
数据库🔥 1094
7
applyparallelworker.c 之 LA 端源码深度解析:Leader Apply Worker 的指挥中枢
数据库深入🔥 1082
8
PostgreSQL Background Worker 全解:从 `RegisterBackgroundWorker` 到逻辑复制 4 类 worker 的全生命周期
数据库🔥 1078
9
PostgreSQL的后台进程walsender分析 - 关系型数据库 - 亿速云
PostgreSQL🔥 1033
10
PostgreSQL 逻辑复制的监控:六张视图 + 一组可执行 SQL,把 publisher/subscriber 的速率与健康度彻底看透
数据库🔥 1032
11
PostgreSQL 逻辑复制支持 DDL 之后:DDL 与 DML 的时序难题(重点:分区表)
数据库🔥 999
12
reorderbuffer.c 源码深度解析:PostgreSQL 逻辑复制的"事务重组引擎
数据库深入🔥 953
13
PostgreSQL 内核开发:读取一张表的 9 步标准流程与缓存全景
数据库🔥 938
14
从 `postgres` 二进制到生产级守护 —— PostgreSQL 最外层模块与启动全流程拆解
数据库🔥 936
15
支持逻辑复制同步 DDL 适配 SQL Server 方案
数据库深入🔥 934
16
PostgreSQL 逻辑复制的 ReorderBuffer 与事务机制:从一行 WAL 到一致性变更流的全链路绑定
数据库🔥 913
17
DDL同步架构(美化版)
数据库深入🔥 908
18
PostgreSQL Latch 机制详解:从一行 SetLatch 到 epoll 的内核之旅
数据库🔥 871
19
pgbench 源码全解:一个 C 文件如何撑起 PostgreSQL 官方压测工具
数据库🔥 860
20
PostgreSQL libpq 机制与缓冲区详解
数据库🔥 850