Apache Iceberg Puffin 文件格式规范详解:结构与实现
大数据数据湖OLAP数据存储【免费下载链接】icebergApache Iceberg项目地址https://gitcode.com/gh_mirrors/iceberg4/iceberg点击查看免费下载本文是 Apache Iceberg 中Puffin 文件格式的完整技术指南讲解这一专用于存储数据索引与统计信息的文件格式的二进制布局、Footer 元数据 JSON 结构、支持的 Blob 类型与压缩编解码器并结合仓库内core模块的读写实现与测试用例深入印证格式细节。读完本文你将能理解 Puffin 文件的字节级结构、自行编写符合规范的 Puffin 文件读写器并掌握 Iceberg 中 Puffin 统计文件StatisticsFile的接入方式。Puffin 是什么为索引与统计而生Puffin 是一种为 Apache Iceberg 设计的文件格式专门用来存放无法直接放入 Iceberg Manifest 中的数据索引和统计信息。一个 Puffin 文件包含若干任意类型的信息片段规范中称为blob以及解释这些 blob 所必需的元数据。Iceberg 当前支持的 blob 类型详见后文 Blob 类型 一节。从 Iceberg 的表模型看Puffin 文件通过StatisticsFile接口被挂载到某个快照上。该接口定义在 api/src/main/java/org/apache/iceberg/StatisticsFile.java 中包含所关联的snapshotId、文件路径、文件总大小、Puffin Footer 的大小以及文件内的BlobMetadata列表。其注释明确指出统计信息是informational的读取器可以选择忽略不依赖统计信息也能正确读取表数据——这保证了 Puffin 作为辅助信息文件的安全性与可选性。文件结构符合规范的 Puffin 文件具有如下整体布局Magic Blob₁ Blob₂ ... Blobₙ Footer其中Magic4 个字节0x50 0x46 0x41 0x31即 ASCII 字符P F A 1取自海雀属学名Fratercula arcticaPuffin与版本号 1位于文件开头Blobᵢ文件中的第 i 个 blob由应用程序依据 Footer 中的元数据自行解释Footer文件尾部定长结构具体见下文。当前 Puffin 格式只有一个版本v1即由Magic末字节0x31所标识的版本。仓库中 PuffinFormat.java 的实现与之完全一致static byte[] getMagic() { return new byte[] {0x50, 0x46, 0x41, 0x31}; }写入时blob 数据按序连续落盘每个 blob 的起始偏移量offset与长度length记录在 Footer 中读取器据此定位并读取任意 blob。测试用例 TestPuffinReader.java 也验证了这一布局第二个 blob 的offset恰好等于第一个 blob 的offset length。Footer 结构Footer 具有如下固定布局Magic FooterPayload FooterPayloadSize Flags Magic各组成部分组成部分长度/类型说明Magic4 字节与文件开头相同的魔数0x50 0x46 0x41 0x31FooterPayload变长描述文件中所有 blob 的 UTF-8 编码 JSON 负载FileMetadata对象可选压缩FooterPayloadSize4 字节整数FooterPayload的字节长度若压缩则为压缩后的长度小端序存储Flags4 字节布尔标志位Magic4 字节结尾魔数与开头相同关于Flags的位定义规范规定字节 0第 1 个字节bit 0最低位FooterPayload是否被压缩其余位保留给未来使用写入时必须置 0其余 3 个字节全部保留给未来使用写入时必须置 0。规范同时明确4 字节整数一律采用有符号二进制补码、小端序存储。对应实现见 PuffinFormat.java 的writeIntegerLittleEndian/readIntegerLittleEndian以及 PuffinWriter.writeFooter 中 Magic payload size flags Magic 的写出顺序。在 PuffinFormat.java 中还可以看到 Footer 尾部定长结构Footer Struct的常量定义static final int FOOTER_STRUCT_PAYLOAD_SIZE_OFFSET 0; // payload 大小字段 static final int FOOTER_STRUCT_FLAGS_OFFSET 4; // flags 字段 static final int FOOTER_STRUCT_FLAGS_LENGTH 4; // flags 占 4 字节 static final int FOOTER_STRUCT_MAGIC_OFFSET 8; // 结尾魔数读取器正是利用这些偏移从文件末尾反向定位并校验 Footer先读末尾定长结构校验结尾Magic再读出FooterPayloadSize反推 Footer 总长度最后从fileSize - footerSize处整段读取 Footer 并校验开头的Magic见 PuffinReader.fileMetadata。Footer PayloadFileMetadata 与 BlobMetadataFooterPayload要么不压缩要么以LZ4 单帧压缩携带内容大小的形式存放其内容是 UTF-8 编码的 JSON表示一个FileMetadata对象。FileMetadataFileMetadata字段如下字段名字段类型必填描述blobsBlobMetadata 对象列表是文件中所有 blob 的元数据列表propertiesJSON 对象字符串值否任意元信息存储如写入方标识/版本规范推荐设置的属性见 Common propertiesBlobMetadataBlobMetadata字段如下字段名字段类型必填描述typeJSON 字符串是blob 类型见 Blob 类型fieldsJSON 整数列表是计算该 blob 所依据的字段 ID 列表列表顺序用于计算 blob 内存储的草图sketchsnapshot-idJSON long是计算该 blob 所依据的 Iceberg 表快照 IDsequence-numberJSON long是计算该 blob 所依据的 Iceberg 表快照的序列号offsetJSON long是blob 内容在文件中的起始偏移量lengthJSON long是blob 在文件中存储的长度若压缩则为压缩后长度compression-codecJSON 字符串否见 压缩编解码器省略表示数据未压缩propertiesJSON 对象字符串值否关于该 blob 的任意元信息对应的 JSON 序列化/反序列化实现位于 FileMetadataParser.java其中 JSON 字段名type、fields、snapshot-id、sequence-number、offset、length、compression-codec、blobs、properties与规范表格逐字对应。测试 TestFileMetadataParser.java 给出了一个含两个 blob 的FileMetadata完整 JSON 示例{ blobs : [ { type : type-a, fields : [ 1 ], snapshot-id : 14, sequence-number : 3, offset : 4, length : 16 }, { type : type-bbb, fields : [ 2, 3, 4 ], snapshot-id : 77, sequence-number : 4, offset : 21474836470000, length : 79834 } ] }同时该测试还印证了校验规则blobs字段缺失会报Cannot parse missing field: blobs非数组会报Cannot parse blobs from non-arrayfields中的整数超出 32 位范围会被拒绝见 TestFileMetadataParser.java 与第 160-177 行。Blob 类型当前规范定义的 blob 类型如下。apache-datasketches-theta-v1这是 Apache DataSketches 库产生的compact Theta sketch的序列化形式。其构造方式为以默认种子default seed构建 Alpha 家族 sketch将每个去重后的单值distinct value使用Iceberg 的单值序列化single-value serialization转换为字节后逐一喂入 sketch。该 blob 的元数据可携带以下属性ndv由 sketch 推导出的去重值数量number of distinct values估计值。仓库中该类型以常量形式定义在 StandardBlobTypes.javapublic static final String APACHE_DATASKETCHES_THETA_V1 apache-datasketches-theta-v1;在实际使用中Spark 读取端的 SparkScan.java 会遍历table.statisticsFiles()当 blob 类型等于APACHE_DATASKETCHES_THETA_V1时用它来辅助估算统计信息——这是该标准 blob 类型在 Iceberg 引擎侧的真实消费场景。压缩编解码器blob 数据可以保持未压缩也可以使用下述编解码器之一压缩为保证最大互操作性其他编解码器不被支持。编解码器名称描述lz4单个 LZ4 压缩帧frame且必须携带内容大小content sizezstd单个 Zstandard 压缩帧且必须携带内容大小仓库中对应枚举定义在 PuffinCompressionCodec.javapublic enum PuffinCompressionCodec { /** No compression */ NONE(null), /** LZ4 single compression frame with content size present */ LZ4(lz4), /** Zstandard single compression frame with content size present */ ZSTD(zstd), }需要留意的是当前仓库实现的实际支持情况在 PuffinFormat.java 的compress/decompress中ZSTD 已通过 aircompressor 的ZstdCompressor/ZstdDecompressor完整落地LZ4 分支仍带有 TODO注明需要 LZ4 frame 压缩器支持。这一限制同样体现在测试中TestPuffinWriter.testEmptyFooterCompressed调用compressFooter()时会抛出Unsupported codec: LZ4见 TestPuffinWriter.java而 zstd 压缩写入/读取有对应的测试资源v1/sample-metric-data-compressed-zstd.bin见 PuffinFormatTestUtil.java。因此在实际使用中blob 与 Footer 的压缩建议使用 zstd或保持未压缩。此外规范还定义了Footer 压缩的默认编解码器为 LZ4FOOTER_COMPRESSION_CODEC PuffinCompressionCodec.LZ4见 PuffinFormat.java当 Flags 的FOOTER_PAYLOAD_COMPRESSED位被置位时读取端默认按 LZ4 帧解压 Footer。通用属性Common properties写入 Puffin 文件时规范推荐在FileMetadata的properties字段中设置以下属性created-by写入该文件的应用程序的可读标识及其版本例如Trino version 381。仓库中以常量定义在 StandardPuffinProperties.javapublic static final String CREATED_BY_PROPERTY created-by;写入端通过 Puffin.WriteBuilder.createdBy 便捷设置。测试中可看到createdBy(Test 1234)写出的文件在读取时fileMetadata.properties()为{created-by: Test 1234}见 TestPuffinReader.java。仓库实现读写 Puffin 文件规范之外Iceberg 在core模块的org.apache.iceberg.puffin包中提供了完整的 Java 读写实现核心 API 集中在 Puffin.java入口如下// 写入创建 PuffinWriter PuffinWriter writer Puffin.write(outputFile) .createdBy(my-app version 1.0) // 设置文件级 created-by 属性 .set(custom-prop, value) // 设置任意文件级属性 // .compressFooter() // 压缩 Footer当前仓库实现会抛 Unsupported codec: LZ4 // .compressBlobs(PuffinCompressionCodec.ZSTD) // 设置 blob 默认压缩 .build(); // 逐个添加 blob writer.add(new Blob( my-blob-type, ImmutableList.of(1), // 计算依据的字段 ID 列表 1234567890L, // snapshot-id 42L, // sequence-number ByteBuffer.wrap(data), PuffinCompressionCodec.ZSTD, // 该 blob 的压缩方式可覆盖默认 ImmutableMap.of(ndv, 1000))); // blob 级属性 writer.finish(); // 或直接 close()隐式 finish writer.footerSize(); writer.fileSize(); writer.writtenBlobsMetadata();写入流程的关键实现见 PuffinWriter.java每个 blob 添加时先写文件头Magic记录当前输出流位置作为fileOffset按blob 自身请求的压缩方式优先、否则使用写入器默认压缩的规则压缩后落盘并在内存中累积对应的BlobMetadata含 offset、压缩后 length、codec 名称。finish()时再整体写出 Footer见 PuffinWriter.finish。// 读取创建 PuffinReader PuffinReader reader Puffin.read(inputFile) // .withFileSize(...) // 已知文件大小时传入可提升读取性能 // .withFooterSize(...) // 已知 Footer 大小时传入可提升读取性能 .build(); FileMetadata fileMetadata reader.fileMetadata(); // 解析 Footer 元数据 for (BlobMetadata blobMeta : fileMetadata.blobs()) { // ... } IterablePairBlobMetadata, ByteBuffer blobs reader.readAll(blobMetadataList); reader.close();读取流程的关键实现见 PuffinReader.javafileMetadata()从文件末尾定位并校验 Footer、解析出FileMetadatareadAll按offset升序对 blob 排序后逐个 seek 读取、按compression-codec解压。若输入流实现了RangeReadable读取 Footer 会使用范围读优化见 PuffinReader.readInput。测试资源与格式验证仓库在core/src/test中提供了完整的格式一致性测试用静态二进制资源比对写入结果可直接作为格式规范的参考样例测试资源位于 core/src/test/resources/org/apache/iceberg/puffin/v1empty-puffin-uncompressed.bin空文件无 blob、Footer 未压缩的参考二进制其 Footer 大小为 28 字节sample-metric-data-uncompressed.bin含两个 blob、未压缩的参考二进制sample-metric-data-compressed-zstd.bin含两个 blob、zstd 压缩的参考二进制Footer 大小为 314 字节。测试类 TestPuffinWriter.java 与 TestPuffinReader.java 分别验证写入字节与参考文件完全一致、读取结果与写入内容完全一致包括 blob 内包含\u0000与 emoji 的二进制数据往返见 TestPuffinReader 第 133-140 行。对于希望自行实现 Puffin 文件读写器的场景建议以 format/puffin-spec.md仓库中的规范原文与本站文档内容一致为准并结合上述测试二进制逐字节核对实现可确保与 Iceberg 生态完全互通。小结Puffin 是 Iceberg 生态中承载索引与统计信息的轻量自描述文件格式它以固定的 4 字节魔数开头、以自描述 JSON Footer 收尾中间连续存放任意数量的 blob每个 blob 通过type、fields、snapshot-id、sequence-number、offset、length等元数据字段被精确定位与解释。当前规范定义了apache-datasketches-theta-v1一种标准 blob 类型与lz4/zstd两种压缩编解码器并通过created-by等通用属性保证文件的来源可追溯。在 Apache Iceberg 中Puffin 文件以StatisticsFile的形式挂载到快照上供查询引擎如 Spark在扫描时辅助估算统计信息同时保持可选择性——任何读取器都可以安全地忽略它。赞分享大数据数据湖OLAP数据存储【免费下载链接】icebergApache Iceberg项目地址https://gitcode.com/gh_mirrors/iceberg4/iceberg点击查看免费下载相关推荐Apache Iceberg Puffin 文件格式规范为索引与统计信息而生的 Blob 存储格式Apache Iceberg Puffin 文件格式规范为索引与统计信息而生的 Blob 存储格式 Puffin 是 Apache Iceberg 定义的一种数据湖大数据数据存储Apache Iceberg Puffin 文件格式完全指南索引与统计数据的标准存储载体Apache Iceberg Puffin 文件格式完全指南索引与统计数据的标准存储载体 Puffin 是 Apache Iceberg 定义的一种文件格式数据湖大数据数据存储safetensors格式规范详解从文件结构到数据类型支持safetensors格式规范详解从文件结构到数据类型支持 safetensors是一种专为机器学习设计的简单、安全的张量存储格式旨在替代不安全的pickl序列化人工智能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考