从 C++ 到 C:pg_clickhouse 0.3 的底层重写为什么值得关注

2026-06-04 22 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:6 分钟

pg_clickhouse 是一个让 Postgres 直接查询 ClickHouse 的扩展,很多团队用它把 ClickHouse 的分析能力"挂"到已有的 Postgres 业务链路上。0.3 版本看起来是个小版本号升级,但底层换了一整块引擎——把 clickhouse-cpp 替换成了同事 Philip Dubé 写的 clickhouse-c,一个 header-only 的 C 客户端库。改动不小,收益也实打实。

C++ 和 Postgres 扩展的"相处问题"

写 Postgres 扩展的老手都知道,C++ 和 Postgres 的错误处理机制天生不对付。Postgres 用 PG_TRY / setjmp/longjmp 做异常跳转,C++ 用 throw / RAII 做栈展开。两者混在一起,一旦 C++ 的 throw 穿越 Postgres 的 longjmp 边界,RAII 析构就不会被调用——资源泄漏、未定义行为、甚至直接 crash。

旧版 pg_clickhouse 依赖的 clickhouse-cpp 是个 C++ 库,这意味着扩展内部始终存在这条"裂缝"。新版换成 clickhouse-c 之后,整条调用链都是 C,和 Postgres 的 setjmp/longjmp 完全兼容,代码路径更稳定,crash 风险大幅降低。

内存分配终于统一了

Postgres 有自己的内存上下文(Memory Context)体系,所有分配和释放都走这个体系,方便在事务结束或错误发生时批量回收。C++ 库自带 new/delete,两套分配模式共存时,要么手动桥接,要么就容易出现"Postgres 上下文管不了 C++ 分配的内存"的问题。

clickhouse-c 支持自定义内存分配函数,pg_clickhouse 直接把 Postgres 的 palloc / pfree 注入进去。从此扩展内所有分配都走 Postgres 内存上下文,生命周期管理干净了很多。

二进制体积和编译速度的变化

去掉 clickhouse-cpp 之后,连带甩掉了 vendored 的 absl 和 cityhash。新依赖只有 liblz4 和 libzstd(加上原有的 libcurl、uuid、libssl),对打包维护者来说更友好——系统包管理器里都有,不用再往扩展里塞一堆第三方源码。

实际数字:

平台 旧版大小 新版大小
macOS (M4) 1.8 MB ~400 KB
x86_64 Linux 4.9 MB ~1.4 MB

作者在 M4 MacBook Pro 上测试,编译、安装、跑完全部测试只要约 2 秒。对 CI 和开发迭代来说,这个差距很实在。

UInt16 转换的 bug 修复

顺手修了一个类型转换 bug:ClickHouse 的 UInt16 之前被转成 Postgres 的 int16(即 smallint),但 Postgres 的 int16 是有符号的,值超过 32767 就溢出。新版改成转成 int32integer),范围足够覆盖 UInt16 的全部值域(0–65535)。

升级实操

这次升级不需要 ALTER EXTENSION UPDATE,也不需要重启 Postgres。重新加载扩展后,下次会话自动生效。下面是一个典型的升级流程:

# 1. 安装新依赖(Debian/Ubuntu 为例)
sudo apt-get install -y liblz4-dev libzstd-dev libcurl4-openssl-dev uuid-dev libssl-dev

# 2. 从 PGXN 安装新版 pg_clickhouse
pgxn install pg_clickhouse

# 3. 编译并安装到当前 Postgres 实例
cd pg_clickhouse-0.3.1
make
make install

# 4. 重载 Postgres 配置(不需要重启)
pg_ctl reload -D /var/lib/postgresql/data

# 5. 在新会话中验证版本
psql -c "SELECT extversion FROM pg_extension WHERE extname = 'pg_clickhouse';"

如果你用的是 Docker 镨像,直接拉取新版即可:

docker pull pgxn/pg_clickhouse:0.3.1

升级后,可以在 Postgres 里直接测试 ClickHouse 查询是否正常:

-- 假设已配置 ClickHouse 连接,测试一个简单查询
SELECT * FROM clickhouse_query('SELECT version()') AS t(version text);

-- 验证 UInt16 不再溢出
SELECT * FROM clickhouse_query(
  'SELECT toUInt16(65535) AS max_u16'
) AS t(max_u16 integer);
-- 期望返回 65535,而不是旧版的溢出值

什么时候该考虑升级

如果你的环境已经在用 pg_clickhouse,这次升级几乎零风险——不需要停服务,底层更稳,体积更小。特别值得升级的场景:

  • 遇到过不明 crash:旧版 C++/longjmp 冲突是潜在原因之一,换 C 底层直接消除。
  • 打包维护者:依赖从 vendored 重库变成系统库,打包复杂度显著降低。
  • CI 频繁编译:2 秒全流程 vs 旧版的漫长等待,迭代效率提升明显。

唯一需要注意的是新增的 liblz4 和 libzstd 依赖——大多数发行版默认就有,但极简环境(比如自定义 Docker 镨像)可能需要手动补上。

一句话总结:版本号看着小,底层换了个更合适的引擎。C 和 Postgres 扩展天然同构,这次重写把"勉强共存"变成了"原生契合"。


相关推荐