news 2026/5/1 10:54:00

我花了一天时间,拆了一下 OpenTeleDB 的 XStore,到底解决了 PG 的哪根老筋?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我花了一天时间,拆了一下 OpenTeleDB 的 XStore,到底解决了 PG 的哪根老筋?

这两年数据库圈有点像3年前的云原生圈:“分布式”、“新一代内核”、“重构存储引擎”这些词突然又密集起来了。

前几天刷群,看到有人转了 OpenTeleDB 的开源消息,说是“基于 PostgreSQL 的新一代内核”。说实话,我第一反应是:又一个魔改 PG?

但看到里面提到一个点:原位更新 + Undo 引擎(XStore),我还是没忍住下了源码。 因为这恰好戳中我这些年被 PG 折磨得最狠的痛点:

表膨胀、autovacuum 抽风、性能像心电图一样忽高忽低。

所以这次我没看 PPT,也没看宣传稿,直接跑到机器上拆了半天,想看看它究竟动了 PG 的哪根“老筋”。

一、先说结论:XStore 不是快,而是“稳”

我装的是 OpenTeleDB 的 17.6 内核版。 创建方式很直观:

SELECT relname, amname FROM pg_class c JOIN pg_am a ON c.relam = a.oid WHERE relname = 'test_xstore';

这一步其实就已经很有意思了——它不是 fork 了一套新引擎,而是作为插件挂进去的。 这个思路我很认可:

  • 不绑死 PG 版本
  • 能跟着大版本升级
  • 出问题可以随时回退

像 Citus、openHalo 这些“成功插件化路线”的项目,本质都是这个思路。

二、打开数据目录,我第一次意识到:它真不是换皮

$PGDATA下面,多了一个非常显眼的目录:

drwx------ 2 postgres postgres 4096 Nov 3 20:15 undo

这就是 XStore 的核心:它不是靠多版本链来维护 MVCC,而是靠 Undo 日志回滚。

这点和 Oracle、MySQL InnoDB 的逻辑更像。

也正是它敢说“原位更新”的底气来源。

三、插入测试:它不快,但很“诚实”

我用同样的参数,在同一台机器上跑了两组:

INSERT INTO test_xstore (name, value) SELECT md5(random()::text), (random()*1000)::int FROM generate_series(1,10000000);
INSERT INTO test_heap (name, value) SELECT md5(random()::text), (random()*1000)::int FROM generate_series(1,10000000);

结果是:

写慢了将近一倍。这点我反而觉得真实:因为 XStore 在写数据页的同时,还要写一份 Undo。物理写入翻倍,吞吐下降是必然的。如果一个系统告诉你“原位更新 + Undo 还更快”,那我反而会不太信。

四、创新实验:模拟1千万数据的存储膨胀对比

我设计了一项创新实验:在 1000 万条级别的大数据量下,评估 XStore 与 Heap 表在高频更新下的空间膨胀、索引稳定性以及查询性能表现。该实验主要有两个创新点:

大规模数据模拟

  1. 使用generate_series(1,10000000)生成 1000 万条数据,保证数据量级对存储膨胀影响明显。
  2. 初始数据包括idnamevalueupdated_at四列,与前期实验一致,但数据量增加十倍,以模拟真实大规模 OLTP 系统负载。

多维度空间分析

  1. 不仅监控表总大小,还分别统计索引占用和 TOAST 表空间。
  2. 每轮更新后,通过pg_relation_sizepg_total_relation_sizepg_indexes_size获取精细化指标。
  3. 引入可视化趋势分析,绘制表空间增长曲线,以直观展示 XStore 与 Heap 的差异。

4.1实验设计

表结构

CREATE TABLE xstore_large ( id SERIAL PRIMARY KEY, name TEXT, value INT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) USING XSTORE; CREATE TABLE heap_large ( id SERIAL PRIMARY KEY, name TEXT, value INT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

初始化 1000 万条数据

INSERT INTO xstore_large (name, value) SELECT 'name_' || g, g FROM generate_series(1, 10000000) AS g; INSERT INTO heap_large (name, value) SELECT 'name_' || g, g FROM generate_series(1, 10000000) AS g;

先对现在存入1000w数据的空间监控与记录一下如下。

xstore_total | xstore_index | heap_total | heap_index --------------+--------------+------------+------------ 985 MB | 388 MB | 789 MB | 214 MB

多轮全表更新

  • 连续 5 轮更新,每轮更新valueupdated_at,模拟写入密集场景:
UPDATE xstore_large SET value = value + 1, updated_at = CURRENT_TIMESTAMP; UPDATE heap_large SET value = value + 1, updated_at = CURRENT_TIMESTAMP;

空间监控与记录

SELECT pg_size_pretty(pg_total_relation_size('xstore_large')) AS xstore_total, pg_size_pretty(pg_indexes_size('xstore_large')) AS xstore_index, pg_size_pretty(pg_total_relation_size('heap_large')) AS heap_total, pg_size_pretty(pg_indexes_size('heap_large')) AS heap_index;

第一轮:

xstore_total | xstore_index | heap_total | heap_index --------------+--------------+------------+------------ 985 MB | 388 MB | 1578 MB | 428 MB

第五轮:

xstore_total | xstore_index | heap_total | heap_index --------------+--------------+------------+------------ 985 MB | 388 MB | 1628 MB | 428 MB

4.2千万数据更新膨胀可视化

五、实验结论

这组 1000 万级数据 + 多轮全表更新的实验,其实把 PG 传统 Heap 表的“老问题”放大得非常清楚。

最核心的对比结果只有一句话:

XStore 的空间是线性的、可预测的;Heap 表的空间是失控的、不可预测的。

具体来看:
1. 表空间膨胀
a. Heap 表在第一次更新后,表体空间直接翻倍,从 789MB 飙到 1578MB。
b. 之后每一轮更新,虽然增长幅度趋缓,但空间再也回不到初始状态。
c. XStore 从头到尾不变: 985MB → 985MB → 985MB
2. 索引体积稳定性
a. Heap 表索引从 214MB 膨胀到 428MB,且在后续更新中保持“高位横盘”。
b. XStore 的索引尺寸始终维持在 388MB 左右,没有明显漂移。
3. 更新行为本质差异
a. Heap:每一次 UPDATE,本质都是 DELETE + INSERT → 老版本残留 → 表膨胀 → 索引碎片 → autovacuum 压力。
b. XStore:真正的原位更新 → 历史版本进 Undo → 主表物理页不变 → 无膨胀。
4. 长期可运维性
a. 在 Heap 表上,如果你不 VACUUM,它一定会慢; 如果你 VACUUM,系统一定会抖。
b. 在 XStore 上,这两件事都不再是必选项。
这意味着什么?

它不是让你飞起来,而是让你不再塌方

六、我的心得

说实话,这几年我已经对“新一代数据库内核”这类说法有点免疫了。大多数项目,要么是在 PG 上糊一层分布式壳; 要么就是换个名字,重新卖一遍 MVCC。而 XStore 给我的感觉不一样。它没有试图掩盖代价。写入更慢, IO 更多,架构更复杂。

但它正面承认了一个事实:

PostgreSQL 的 MVCC,在高频更新场景下已经接近物理极限。

这不是参数调优能解决的事,也不是加机器能扛住的事,而是存储模型本身的问题。这些年我见过太多系统:白天 QPS 很稳,半夜 autovacuum 开始清垃圾,延迟突然拉长,业务报警,DBA 开始手工 VACUUM / REINDEX / CLUSTER,第二天继续循环。

这不是运维水平的问题,而是模型在和现实硬扛。XStore 让我第一次意识到:原来 PG 也可以选择不走这条老路。它没有追求“更快”,而是选择了一个更难、但更稳的方向:

  • 用 Undo 换空间可控
  • 用写放大换性能平滑
  • 用工程复杂度换系统长期可预期性

如果你是写多、更新密集型 OLTP 系统,如果你被表膨胀、索引碎片、autovacuum 抽风折磨过,那你会和我一样—不一定立刻用它,但你会开始认真看它。这大概就是我这次拆源码、跑实验,最大的收获。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 3:43:50

提示工程架构师工具选型:破解Agentic AI技术挑战的6款必备开源框架

提示工程架构师工具选型:破解Agentic AI技术挑战的6款必备开源框架 一、引入与连接:当Agentic AI遇到“能力边界” 1. 一个真实的“Agent崩溃现场” 某电商平台的智能导购Agent上线3天就收到了127条投诉: 用户问“这件衣服洗了会缩水吗&a…

作者头像 李华
网站建设 2026/5/1 5:47:31

2026年大模型16项核心Benchmark全解析,选型不再迷茫

本文详解2026年大模型评估体系中的16项全维度核心Benchmark,涵盖通用常识、学术研究、编程实战等能力维度。通过指标详解、术语解码和案例拆解,帮助读者理解如何根据自身需求选择合适模型,并给出避坑指南,强调匹配度比平均分更重要…

作者头像 李华
网站建设 2026/5/1 4:46:38

2025年DT SCI1区TOP,无人机编队鲁棒多目标任务规划: 融合任务预分配与重分配框架,深度解析+性能实测

目录1.摘要2.无人机编队任务的预先分配与重新分配问题3.改进多目标进化算法4.结果展示5.参考文献6.代码获取7.算法辅导应用定制读者交流1.摘要 针对无人机编队在任务执行过程中因平台损毁或目标未完成而导致作战效能下降的问题,本文提出了一种融合任务预分配与重分…

作者头像 李华
网站建设 2026/5/1 4:46:01

米尔顿·弗里德曼《实证经济学方法论》解读

米尔顿弗里德曼《实证经济学方法论》解读 米尔顿弗里德曼的《实证经济学方法论》是1953年收录于其《实证经济学论文集》的开篇纲领性论文,是现代实证经济学发展的基石性文献。该文以工具主义哲学为内核,打破了传统经济学方法论的混乱局面,确…

作者头像 李华
网站建设 2026/5/1 4:42:23

数据立方体在智慧城市建设中的关键作用

数据立方体:打通智慧城市数据壁垒的核心引擎 摘要/引言:当智慧城市遇到“数据孤岛”难题 清晨7点,深圳南山区的上班族小李盯着手机上的导航APP——屏幕上红得发亮的拥堵路段像一条凝固的血管。与此同时,3公里外的环保监测站发出…

作者头像 李华