Postgres 的生态正经历一次微妙的重构:Snowflake、Lakebase、HorizonDB 三家平台先后发布了基于 Postgres 协议和接口的数据库产品,但底层存储引擎和扩展架构全部自研。换句话说——你写的是 Postgres SQL,跑的却不再是社区版 Postgres。
这不是简单的"兼容层"包装,而是把 Postgres 的查询引擎嫁接到完全不同的存储与分布式架构上。对开发者来说,这意味着一个新选择:接受哪种锁定?
换芯不换壳:为什么各家都在做这件事
社区版 Postgres 有两个长期痛点:
- 存储扩展性受限:单实例存储上限受 checkpoint 和 WAL 机制约束,TB 级以上场景运维成本陡增。
- 水平扩展靠插件:Citus、Patroni 等方案成熟度不错,但本质上是在社区版之上"搭桥",架构复杂度不会消失。
三家平台的思路一致:保留 Postgres 的 SQL 语法、驱动协议、工具链兼容性(pg_dump、psql、JDBC/ODBC),把存储和分布式层换成自己的引擎。用户迁移成本极低——连接字符串改个 host,SQL 不用改——但运行时行为已经完全不同。
三条路径,三种锁定
| 维度 | Snowflake Postgres | Lakebase | HorizonDB |
|---|---|---|---|
| 存储引擎 | Snowflake 内部列存 + 云对象存储 | Lakehouse 架构(Parquet/Iceberg 底层) | 自研分布式存储(传闻为行存优化) |
| 扩展模型 | Snowflake 弹性集群,自动扩缩容 | 计算存储分离,计算节点按需拉起 | 分片 + 多主写入 |
| 锁定点 | Snowflake 生态(数据共享、Marketplace) | Lakehouse 格式锁定(开放格式但运维依赖平台) | 自研存储协议,迁移需导出 |
| 典型场景 | 分析型查询混入 OLTP、数据共享 | 数据湖上跑 Postgres 查询、湖仓一体 | 高并发写入、地理分布部署 |
核心差异在存储层。Snowflake 把自家列存引擎接上 Postgres 解析器,适合重分析场景;Lakebase 走开放格式路线,Parquet/Iceberg 文件可直接被 Spark/Flink 读取,但写入路径依赖平台;HorizonDB 更偏 OLTP,自研存储追求低延迟写入,代价是迁移时没有标准格式可导出。
实际迁移:改一行连接字符串够不够?
理论上够。实际不够。以下是一个典型评估流程,用 psql 和 Python 快速验证三家平台的兼容性边界。
第一步:用 psql 检查语法兼容性
# 连接到目标平台(连接字符串各平台文档会给出)
psql "host=snowflake-pg.us-east-1.snowflakecomputing.com port=5432 dbname=mydb user=admin password=***"
# 运行兼容性探测脚本
-- 1. 基础 DDL
CREATE TABLE compat_test (
id BIGSERIAL PRIMARY KEY,
data JSONB DEFAULT '{}',
ts TIMESTAMPTZ DEFAULT now()
);
-- 2. 核心函数兼容性
SELECT now(), gen_random_uuid(), to_jsonb(row(1, 'hello'));
-- 3. 窗口函数
SELECT id, ts, lag(ts) OVER (ORDER BY id) FROM compat_test;
-- 4. CTE + 递归(递归 CTE 是兼容性高频断点)
WITH RECURSIVE r AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM r WHERE n < 5
)
SELECT * FROM r;
-- 5. 清理
DROP TABLE compat_test;
逐条执行,记录报错。递归 CTE、JSONB 操作符、gen_random_uuid() 是最容易出差异的地方。如果某条报错,说明该平台在解析层就做了裁剪,后续复杂查询大概率也会踩坑。
第二步:用 Python psycopg2 跑参数化写入
import psycopg2
import time
# 替换为你测试平台的连接参数
CONN = {
"host": "your-platform-endpoint",
"port": 5432,
"dbname": "testdb",
"user": "admin",
"password": "your-password",
}
def bench_write(n=5000):
conn = psycopg2.connect(**CONN)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS bench (
id BIGSERIAL PRIMARY KEY,
payload TEXT,
created TIMESTAMPTZ DEFAULT now()
)
""")
conn.commit()
start = time.perf_counter()
for i in range(n):
cur.execute(
"INSERT INTO bench (payload) VALUES (%s)",
(f"record-{i}",),
)
conn.commit()
elapsed = time.perf_counter() - start
print(f"写入 {n} 条耗时: {elapsed:.2f}s | QPS: {n / elapsed:.0f}")
cur.execute("SELECT count(*) FROM bench")
print(f"确认行数: {cur.fetchone()[0]}")
cur.execute("DROP TABLE bench")
conn.commit()
cur.close()
conn.close()
bench_write()
这段脚本测试的是单连接批量提交的吞吐。三家平台存储引擎不同,INSERT → commit 路径的延迟差异会非常明显——列存引擎(Snowflake)通常在单行写入上较慢,行存引擎(HorizonDB)会更快。如果你的业务是高频单行写入,这个数字比任何架构文档都靠谱。
第三步:检查扩展(Extension)可用性
# 在目标平台执行
SELECT name, default_version, installed_version
FROM pg_available_extensions
ORDER BY name;
社区版 Postgres 有上百个扩展(pg_stat_statements、pg_trgm、postgres_fdw 等)。换芯版本大概率只保留一小部分。如果你依赖 pg_trgm 做模糊搜索或 postgres_fdw 做跨库查询,这一步会直接告诉你能不能用。
锁定的真实代价:不只是迁移困难
选择锁定时,开发者常只考虑"能不能迁走",但更隐蔽的代价在运行时:
- 性能模型变化:社区版 Postgres 的
VACUUM、checkpoint行为你已经熟悉;换芯后这些机制可能不存在,取而代之的是平台内部的垃圾回收和持久化策略。你过去的调优经验不再适用。 - 监控体系断裂:
pg_stat_activity、pg_stat_statements可能被替换成平台自有监控 API。Grafana/PgHero 那套仪表盘需要重建。 - 升级节奏失控:社区版由 PG 社区发版,你控制升级窗口;换芯版本由平台决定,可能在你不知情的情况下完成存储格式升级。
选择清单
在做最终决策前,跑一遍这个清单:
| 检查项 | 怎么验证 |
|---|---|
| 核心 SQL 兼容性 | 上文 psql 探测脚本全通过? |
| 必需扩展可用性 | pg_available_extensions 查询结果覆盖你的依赖? |
| 写入延迟可接受 | Python 基准测试 QPS ≥ 你的峰值需求? |
| 数据可导出为标准格式 | 能导出为 CSV/Parquet/SQL dump,不依赖平台专有工具? |
| 监控 API 有文档 | 平台提供等效 pg_stat_* 的替代 API 且有公开文档? |
| 升级通知机制 | 平台承诺提前 N 天通知存储/引擎变更? |
| 退出路径明确 | 有官方迁移指南,且有人实际走过? |
如果前四项全部通过,锁定是可控的——你用 Postgres 协议写代码,数据随时能以标准格式迁走。如果"数据可导出"这一项失败,锁定就是实质性的:即使 SQL 兼容,你也被困在平台的存储格式里。
三家平台同时押注"Postgres 接口 + 自研存储",说明市场已经形成共识:Postgres 的 SQL 和协议是事实标准,存储引擎是差异化战场。作为用户,你不需要拒绝锁定——你只需要确保锁定的那一环,是你主动选的,且随时能解开。