暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

内核探究|Apache Cloudberry™ Directory Table 底层逻辑与实现原理讲解

ApacheCloudberry 2025-04-11
125

Apache Cloudberry™ (Incubating) 是 Apache 软件基金会孵化项目,由 Greenplum 和 PostgreSQL 衍生而来,作为领先的开源 MPP 数据库,可用于建设企业级数据仓库,并适用于大规模分析和 AI/ML 工作负载。

GitHub:  https://github.com/apache/cloudberry

文章作者:张文超,Apache Cloudberry 贡献者;整理:酷克数据


面对 AI 应用带来的非结构化数据管理挑战,Apache Cloudberry 引入了一种专门用来存储管理非结构化数据的新的表类型—— Directory Table(目录表)。

通过 Directory Table 我们可以将各式各样非结构化的数据统一纳管起来,并且可以结合酷克数据自主研发的下一代 In-Database 高级分析和数据科学工具 HashML,对存储在 Directory Table 中的数据进行挖掘学习,为用户提供更进一步的价值。

01 Tablespace 

在介绍 Directory Table 之前,我们首先需要理解数据库中的一个经典概念:Tablespace,它指的是数据库文件存储的本地目录地址,例如 code/base/。

在 PostgreSQL 中,其默认的 Tablespace 是 pg_default,对应的数据目录是 base 目录,可以通过 default_tablespace 的 guc 来修改默认 tablespace。

相对于传统的 Local Tablespace,Cloudberry 引入了一种新的 Tablespace,用于存储分布式文件系统(DFS)的文件存储地址。
  • 以对象存储为例:/bucket_name/tablespace-1/;

  • 以 HDFS 为例 :/usr/warehouse/tablespace-1/;

在创建表时,我们可以指定该表使用的 Tablespace。如果不指定,它将使用默认的 Tablespace。而本期我们要讲解的 Directory Table,实际上就是依赖于 Tablespace 来实现的。接下来,我们将通过实际操作来演示 Tablespace 的使用。



02 Directory Table 实现原理 


图 1 Directory Table 存储架构

上图展示了一张关于 Directory Table 存储架构的示意图。从图中可以清晰地看到,系统提供了两种类型的 Directory Table:Local Directory Table(本地目录表)和 Remote Directory Table(远程目录表)。

当处理非结构化数据时,我们可以通过执行 CopyFrom 命令或使用 gpdirtableload 工具将其导入到 Directory Table 中。导入的数据将被存储在 Tablespace 中。

  • 如果使用的是 Local Directory Table,数据将存储在 Local Tablespace(本地表空间)中;
  • 而如果使用的是 Remote Directory Table,数据则会存储在 DFS Tablespace(分布式文件系统表空间)中。

Directory Table 主要涉及三个与元数据相关的操作部分,分别是:Index(索引)、Catalog(目录)以及 Schema table(模式表)。

  • 如下图所示,Catalog 中包含了一张名为 pg_directory_table 的系统表。这张系统表包含三个关键字段:DT_OID,用于唯一标识 Directory Table 的对象标识符(OID);Tablespace 的 OID,用于标识表空间;以及 Directory Table 的具体文件路径。

图 2 Catalog 表结构图


  • Schema table 可以理解为一张普通 Heap 表,专门用于记录 Directory Table 中的层级信息,从而管理 Directory Table 中存储的所有非结构化数据。Schema table 主要包含五个字段:

    • relative_path,即文件的相对路径,也可以理解为文件在 Directory Table 中的名称;

    • size,表示文件的大小;
    • upload_time,记录文件上传的时间;
    • MD5,用于文件的完整性校验;
    • 以及 tag,允许用户在上传文件后添加标签,以便更方便地管理和检索这些非结构化数据。

图 3 Schema table 结构图

03 创建 Directory Table 

图 4 创建 Directory Table 流程图


上图为创建 Directory Table 的代码流程图,其中要注意以下几点:
  • Directory Table 的 Schema 表按照 relative path 列进行分布,上传的非结构化数据文件,会基于分布列将数据分布存储到 segment 中。
  • 主键为 relative path,在插入非结构化数据时,relative path 相同的拒绝插入。同时可以加速 Schema 表的查询效率。
  • Directory Table 的权限检查基于 Schema 表。
  • 创建成功后,会在 pg_directory_table 系统表中插入一条对应 tuple,同时会在对应的 Tablespace 下生成对应目录。
  • Schema 表禁止除 Update tag 列外的一切 DML 操作。


04 导入 Directory Table 



接下来我们一起看下 Copy 导入非结构化数据文件到 Directory Table 的数据流向图,以及具体演示。


图 5 Copy 导入非结构化数据流向图



下图是关于 Copy 代码的一个流程:首先,系统会判断操作是否通过 CopyFrom 接口发起,并检查所操作的表是否为 Directory Table,若不是 Directory Table,则执行常规的 Copy table 逻辑;若是,则进一步判断其类型为 QD 还是 QE,分别执行发送数据或接收数据的流程。最后,无论执行了哪种类型的操作,都会进行必要的清理工作,以确保资源得到正确释放和状态得到更新。


图 6 Copy 代码流程


QD 具体的执行流程如下:


图 7 Copy 导入 - QD 流程


  • CdbCopyStart 会执行 buildGang,建立 QD→QE 之间的 connection,通过 pg 中的 libpq 来传输数据。

  • ReadBinaryData 会从 libpq 中读取二进制数据流。

  • Md5Sum 会动态计算 md5,直至文件传输完成得到整个文件的 md5。

  • ComputeTargetSeg 会基于 relative path 分布键计算 tuple 需要发送的 QE。

  • CdbCopyEnd 会等待 Copy 到 QE 过程的结束,并收集 QE 返回的状态和统计信息。

接下来是 QE 的执行流程:


图 8 Copy 导入 - QE 流程


  • NextCopyFromExecute 会从 libpq 中读取 QD 发送的数据并存到 TupleTableSlot 中。

  • Index Tuple Check 会基于主键检查当前 tuple 是否已经在 index 中存在,这里主键为 relative_path,可以避免相同文件名覆盖。

  • UFileAddCreatePendingEntry 会添加一个 PendingDelete 对象到链表中,类似于 pg 中 pending delete 的机制,该链表会在事务提交将数据文件落盘,在事务回滚时删除数据文件,保证了数据文件的事务性。

  • UFileOpen/UFileWrite/UFileSync/UFileClose 是数据文件操作的统一接口,通过这些接口可以实现对底层存储介质的透明,如本地存储或者远端对象存储。

‍‍‍‍那么如何在 Directory Table 中查询数据呢?如下图所示,我们提供了一些 UDF(用户定义函数)来进行查询。


图 9 查询数据流程


QD 调用 Directory Table 的函数。函数执行时,QD 会向 QE 发送查询计划。QE 收到计划后执行它,并调用 FunctionScan 来扫描 schema 表,找到要查询的那条 tuple,并获取具体的文件路径或 URL(本地文件为路径,远端文件为 URL)。


QE 拿到这个路径或 URL 后,会到 Tablespace 找到对应的 Directory Table,然后找到对应的非结构化数据,紧接着给 QD 返回一个 TupleTableSlot,而返回的数据通过 Motion 的 UDP 传输方式,保证了传输的可靠性。


05 删除数据


既然存在数据的传输,那么也必然涉及到数据的删除操作。我们主要关注的是删除操作的交互流程,这一流程通过 remove_file() 的函数来实现的。如下图所示:

图 10 删除数据流程


当 QD 调用这个函数时,它会将删除命令发送给 QE。QE 收到命令后,会调用 remove_file segment 函数,执行一个顺序扫描,扫描 schema 表以找到要删除的 tuple,并获取对应的路径或 URL 。


随后,QE 会构造一个 pending delete 对象,并将其添加到 pending delete 链表中,同时在 schema 表中删除对应的 tuple。这个 pending delete 机制在 commit 或 rollback 时决定文件的最终状态。


06 删除 Directory Table 

最后,展示最删除 Directory Table 的流程图,这里需要注意:

  • aclcheck 会对权限进行检查,只有超级用户或者是 Directory Table 的 owner 才有权限执行删除。

  • heap_drop_with_catalog 会在 pg_class 元数据的 tuple 进行删除。

  • RemoveDirectoryTableEntry 会将 pg_directory_table 系统表中对应的 tuple 删除。

  • UFileAddDeletePendingEntry 类似于 UFileAddCreatePendingEntry,在执行删除时会添加一个 PendingDelete 对象到链表中,该链表会在事务提交将数据文件删除,在事务回滚时不做处理。

图 11 删除 Directory Table 的流程


推荐阅读


👇🏻️扫码加入 Apache Cloudberry 交流群👇🏻️

文章转载自ApacheCloudberry,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论