Apache Cloudberry™ PAX 行列混存方案技术解析
Apache Cloudberry™ (Incubating) 是 Apache 软件基金会孵化项目,由 Greenplum 和 PostgreSQL 衍生而来,作为领先的开源 MPP 数据库,可用于建设企业级数据仓库,并适用于大规模分析和 AI/ML 工作负载。
文章作者:周嘉祺,Apache Cloudberry PPMC 成员;整理:酷克数据
在传统的分析场景(如聚合,过滤,排序等)中,列存储通过减少不必要的 I/O,实现了出色的性能。但对于宽表点查、记录快速更新等场景时,行存储则可以规避 IOPS 过多,获得更优秀的实时能力。如何在一套数仓架构下同时实现行列混存,从而在事务处理与分析查询之间找到平衡呢?
Apache Cloudberry™ 推出的 PAX 存储引擎正是面向这个需求的创新实践。PAX 通过行列混存设计、多版本并发控制(MVCC)、灵活的编码压缩算法等特性,显著提升了存储效率和不同场景下的查询性能。
本文将从架构设计、核心特性及实例操作等方面,深入解析 Cloudberry PAX 的技术实现,并探讨其在实际场景中的应用价值。
PAX 源码目录:https://github.com/apache/cloudberry/tree/main/contrib/pax_storage
01 概述
PAX,作为 Cloudberry 的行列混存存储引擎,其核心设计理念在于精妙地平衡数据布局与存储策略,以同时满足事务处理的敏捷性与复杂查询的深度需求。Cloudberry PAX 引擎的几大核心功能亮点:
行列混存设计:在文件内部按列组织数据,同时支持按行或按列的高效访问,为多样化的数据访问模式提供强有力支持。这种设计使得 PAX 在处理 OLTP(在线事务处理)和 OLAP(在线分析处理)混合负载时表现出色。
完整的 PostgreSQL 类型支持:涵盖基础类型(如整型、布尔型)、复合类型(如数组、范围类型)及用户自定义类型。这一特性确保了 PAX 与 PostgreSQL 生态系统的无缝集成。
编码与压缩:支持多种算法(如 rlev2、字典编码、zstd 压缩),并根据数据类型动态选择最优策略。这显著减少了存储空间的占用,提高了数据访问速度。
统计信息与稀疏过滤:通过文件级和组(Group)级统计信息(如 min/max、布隆过滤器),快速跳过无关数据,减少了不必要的数据扫描。
向量化执行支持:兼容行式执行器和向量化执行器,减少了数据转换开销,提高了查询性能。
02 架构设计
1. 分层架构
PAX 的架构分为多层,各层协同实现高效的数据管理:

图 1 PAX 分层架构
Access Handler 层:负责对接 TABLE AM 进行交互。由于 PAX 使用 CI(Compaction Interface)加写模式,Access Handler 执行了与行引擎的连接,处理异常转化。此外,Access Handler 还负责资源的管理,尤其是在筛选过程中需要处理的资源,如描述符(Describe)资源和文件描述符(FD)。
Table Layer 层:负责表级别的读写操作,支持行式和向量化接口。这一层提供了对表数据的直接访问和操作功能。
MicroPartition Layer 层:管理文件(Block)和组(Stripe)级别的数据操作,支持动态分片与统计信息生成。这一层是 PAX 实现高效数据存储和访问的关键。
Column Layer 层:列数据的内存抽象,处理编码、解码及内存对齐。这一层确保了列数据在内存中的高效表示和处理。
File Layer 层:文件系统抽象,支持数据文件、元数据及可见性映射(Visibility Map)的持久化。这一层负责将数据存储到物理文件中,并管理文件的元数据。
2. 元数据管理
元数据主要由 PAX 辅助表(Heap AM)中的记录构成。每一个文件都对应辅助表中的一条记录。这条记录详细描述了文件的各种属性,以便引擎能够高效地管理和访问这些数据。
辅助表主要由以下几个字段组成:
此外,还有一个重要的表是 fast sequence 表。这个表用于生成全局的 block name(即文件名)。因为文件名是按照从 0 开始递增的整数来命名的,并且与 CTID 相关,所以需要一个全局的值来确保在多线程或分布式环境下生成的文件名不会重复或冲突。这个全局值就是由 fast sequence 表来提供的。
3.MVCC 实现
PAX 通过结合 Heap 的 MVCC 机制和可见性映射表(visimap)来实现多版本并发控制(MVCC),确保不同事务在访问数据时互不干扰。
Heap 的 MVCC 机制:PAX 继承了 Heap 的 MVCC 设计,通过为每个数据行维护多个版本来支持并发操作。每个事务只能看到与其相关的数据版本,从而避免了读写冲突。
可见性映射表(visimap):visimap 是 PAX 用来管理数据文件可见性的关键组件。每个 visimap 文件的命名规则如下:
blocknum:当前数据文件的唯一标识(例如文件名),用于快速定位物理存储位置。
generation:visimap 的代数(版本号)。每次对数据文件执行删除操作时,代数会 +1,用于区分不同修改状态。
tag:当前事务的唯一 ID,确保不同事务生成的 visimap 文件名不会重复。
辅助表的作用:PAX 通过辅助表记录哪些数据文件对当前事务可见。辅助表中的每一行对应一个数据文件,结合 visimap 文件,事务可以快速判断哪些数据版本是可见的。
工作流程事务启动时:系统为事务分配唯一的事务 ID(tag),并生成对应的 visimap 文件。数据修改时:删除或更新操作会生成新的 visimap 文件(generation+1),标记哪些数据行对当前事务可见。数据读取时:事务通过查询辅助表和 visimap 文件,快速定位可见的数据版本。
4.数据格式设计
PAX 的数据文件格式是基于 ORC 格式改进而来的,但针对 Cloudberry 的需求做了很多优化,重新设计 DataStream 和 MetaStream、独立存放统计信息、支持两种存储格式(PORC 和 PORC_VEC),能够更好地满足事务处理和分析查询的需求:

图 2 数据格式文件组成
统计信息独立存放:PAX 将统计信息(如最大值、最小值)单独存储,而不是像 ORC 那样放在列数据中,这样可以更快地访问和过滤数据。
去掉了 Index Data:PAX 移除了 ORC 中的索引数据,简化了文件结构,减少了存储开销。
重新设计的列类型:PAX 优化了列类型的存储方式,使其更高效。
自定义 Attributes:PAX 允许为列添加自定义属性,方便扩展和优化。
重新设计 DataStream 和 MetaStream:PAX 重新设计了这两种数据流的结构,使其更适合 Cloudberry 的执行器。DataStream:存储实际的列数据,与内存中的列结构(Column 抽象)一一对应。MetaStream:存储与 DataStream 相关的元信息,比如数据的描述和结构。
PAX 支持两种数据格式,分别针对不同的执行器:PORC 格式:适合 Cloudberry 的行式执行器,按行存储数据,适合事务处理场景。PORC_VEC 格式:适合 Cloudberry 的向量化执行器,按列存储数据,适合分析查询场景。这两种格式在写入时就会进行转换,避免读取时的额外开销。
5. Column 结构
在 PAX 中,Column 是内存中用来管理列数据的结构。它负责将磁盘上的数据映射到内存中,并在读写时进行必要的格式转换。

图 3 Column 架构
映射磁盘数据:Column 是磁盘数据在内存中的表示形式。当从磁盘读取数据时,Column 会将数据加载到内存中;当写入数据时,Column 会将内存中的数据写回磁盘。
格式转换:在读写数据时,Column 会根据需要将数据从磁盘格式转换为内存格式,或者反过来。这种转换确保了数据在不同存储介质之间的高效传输。
编码与压缩支持:Column 支持多种编码和压缩算法(如 rlev2、字典编码、zstd 压缩),以减少存储空间并提高读写效率。
灵活的读写接口:按单行读写:适合事务处理场景,逐行处理数据。按批量读写:适合分析查询场景,一次处理多行数据,提升效率。
内存对齐与复合类型对齐:内存对齐:Column 确保数据在内存中按照 CPU 缓存的要求对齐,提升访问速度。复合类型对齐:对于复杂数据类型(如数组、范围类型),Column 会特别处理其内存布局,确保高效访问。
03 PAX 核心特性
1.编码与压缩算法
PAX 为了提高存储效率和查询性能,会对 Group(stripe) 中的列进行编码或压缩操作。
2.稀疏过滤
PAX 通过稀疏过滤机制可以快速跳过无关数据,从而加快查询响应。在列存在统计信息的情况下,默认会打开稀疏过滤。
文件级过滤/组级(Stripe)过滤:基于文件/stripe 内记录的 min/max 值,快速排除不符合条件的文件。例如,查询“年龄<18”时,直接跳过只存在 min_age≥18 的文件/stripe。或是基于布隆过滤器(Bloom Filter)排除不匹配的数据块,例如,筛选“用户 ID=10086”时,可精确跳过不含该 ID 的 Stripe。
优化建议:若数据按分区写入或经过 Z-Order/Lexical 排序(Cluster 优化),稀疏过滤效率可进一步提升。
3.分区写入与 Cluster 优化
PAX 通过智能数据分布策略,为查询性能奠定基础:
分区写入:按列的 min/max 范围(如时间戳、地域 ID)将数据分文件存储。例如,将 2023 年与 2024 年的订单数据写入不同文件,查询时可直接定位目标文件,减少扫描范围。
Cluster 优化:Z-Order 排序:将多维数据(如经纬度、时间)映射为一维顺序存储,显著提升范围查询效率(如“某时间段内某区域的订单”)。Lexical 排序:按多列字典序排列(如先按省份、再按城市),适合等值查询(如“北京市海淀区的用户”)。Cluster 后的数据物理分布更有序,稀疏过滤命中率更高,复杂查询响应速度可提升数倍。
04 实例解析
1. PAX 写入流程
如下图所示(红色部分表示必经之路,绿色部分表示可选路径),数据的写入过程始于通过 AM(访问方法)传入 PAX TableWriter。在写入前,系统会决定是创建新的文件以更新元数据,还是使用已打开的文件。写入完成后,元数据会得到更新。

图 4 PAX 写入流程
接下来,数据会进入 MicroPartition Writer 阶段。在这里,系统会决定当前数据应写入哪个 Group(Stripe),并判断是否需要对部分列生成统计信息。同时,还会决定是否需要生成 Toast(用于存储大字段的机制)。
随后,数据进入 Column 阶段。系统会决定将数据写入哪个 Buffer,并进行格式转换、对齐等操作。接着,系统会选择合适的编码和压缩方式对数据进行处理。
最后,在 File 阶段,经过处理的 Buffer 数据会聚合成单个 IO 操作,写入到文件中。
2. PAX 读取流程

图 5 PAX 读取流程
数据的读取可以通过 AM/Vec Parallel 调用 PAX TableReader 来实现。首先,系统会读取元数据,获取可见文件列表以及统计信息。然后,根据元数据创建迭代器。
在 MicroPartition Reader 阶段,系统会根据选项决定使用哪些 reader(装饰器)。如果存在统计信息,将会进行稀疏过滤,以优化读取性能。接着,系统会打开并读取当前需要被读的文件,包括 VM(visiable map) 和 Toast。
在 Column 阶段,系统会根据文件的自描述部分创建 Columns。最后,在 File 阶段,系统会产生读 IO 操作,将数据按行或 batch(列式)返回。
3. PAX 删除操作
下图展示了使用 VM 的方式进行删除操作。删除操作(通过 CTID 标识)会通过 AM 调用到 PAX TableDeleter。系统会通过 CTID 获取对应的 blockno,并进行标记删除。

图 6 PAX 删除流程
在 DML(数据操纵语言)操作完成阶段,系统会更新 VM 和元数据,以反映删除操作的结果。同时,还会更新统计信息,以保持数据的一致性和准确性。需要注意的是,删除操作实际上只对 VM 进行更新,而不对文件本身进行直接删除或修改。
05 总结
Cloudberry PAX 通过行列混存设计、灵活的编码压缩策略及高效的稀疏过滤机制,为混合负载场景提供了理想的存储解决方案。未来,Cloudberry 社区将进一步优化动态统计信息采集、增强分布式场景下的数据分片策略,并扩展更多编码算法以适应多样化数据类型。
评论