在现代数据湖和分析管道中,Apache Parquet 已成为标配的列式存储格式,但传统 Java 解析器如 parquet-java 依赖 Hadoop 栈且单线程,难以发挥多核 CPU 潜力。Hardwood 作为全新实现,以 Java 21 为目标,提供零 Hadoop 依赖的高性能解析,其核心在于页级(page-level)多线程解码和自适应预取机制,实现最小内存占用下的高吞吐。
Parquet 文件结构为行组(row group)→ 列块(column chunk)→ 数据页(data page),每个页包含值、重复 / 定义级别(RL/ DL)和压缩元数据。Hardwood 的页级并行主义将单个列块的页解码任务分发至多个 worker 线程,利用所有 CPU 核心,而非粗粒度的行组或文件级并行。这避免了列间同步开销,并通过 memory-mapped I/O 最小化拷贝。证据显示,在 NYC 黄出租车数据集(9.2 GB,650M 行,平坦 schema),使用 ColumnReader 多文件模式,仅求和 3 列即达 580M 行 / 秒(M3 Max 16 核,1.12 秒平均)。
自适应预取是另一关键:监控各列页解码速率,动态为慢列(如复杂类型)分配更多预取资源,确保所有列同步推进。同时,跨文件预取(cross-file prefetching)在处理文件 N 末尾时提前映射文件 N+1,消除文件切换 stall。Overture Maps POI 文件(900 MB,9M 行,嵌套 schema)全列解析仅需 1.27 秒(7M 行 / 秒),证明了机制在嵌套场景的有效性。
工程化落地参数与配置
-
依赖引入(Maven 示例,最小 deps):
<dependency> <groupId>dev.hardwood</groupId> <artifactId>hardwood-core</artifactId> <version>1.0.0.Alpha1</version> </dependency> <!-- 可选压缩:Snappy --> <dependency> <groupId>org.xerial.snappy</groupId> <artifactId>snappy-java</artifactId> </dependency>使用 BOM 统一版本管理,避免版本冲突。
-
JVM 优化(Java 21+,推荐 25):
参数 值 作用 --add-modules jdk.incubator.vector- 启用 Vector API SIMD 加速解码(如 null 计数、字典查找),256-bit AVX2 下提速显著 --enable-native-access=ALL-UNNAMED- libdeflate 加速 GZIP(Java 22+,需系统安装 libdeflate-dev) -Dhardwood.simd.disabled=false默认 确保 SIMD 启用 -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints可选 JFR 详细追踪 -
线程池与读取模式:
- 共享线程池:
Hardwood hardwood = Hardwood.create();默认线程数 ≈ availableProcessors () - 1。 - 高吞吐首选 ColumnReader:返回 primitive 数组(如
double[]),支持 JIT 向量化,避免 per-row 虚调用。 示例(多文件列求和):try (Hardwood h = Hardwood.create(); MultiFileParquetReader pr = h.openAll(files); var cols = pr.createColumnReaders(ColumnProjection.columns("col1", "col2"))) { var col1 = cols.getColumnReader("col1"); double sum = 0; while (col1.nextBatch()) { double[] vals = col1.getDoubles(); BitSet nulls = col1.getElementNulls(); for (int i = 0; i < col1.getRecordCount(); ++i) { if (nulls == null || !nulls.get(i)) sum += vals[i]; } } } - 投影(projection):
ColumnProjection.columns("col1")跳过无关列,I/O/ 解码节省 50%+。
- 共享线程池:
-
调优参数清单:
参数 默认 / 推荐 场景 线程数 CPU-1 大文件多核 批次大小(batch size) 内部动态,~64K 值 / 批 调大减开销 预取深度(prefetch depth) 自适应,监控 JFR 慢列设高(SSD vs HDD) 文件上限 / 文件 2GB 大数据集拆分多文件
监控与回滚策略
集成 JDK Flight Recorder(JFR)追踪关键指标:
- Prefetch misses:高则增预取深度或优化存储。
- Page decoding times:列间不均 → 检查压缩 / 编码。
- Thread pool starvation:增线程或减并发。
示例启用:-XX:StartFlightRecording=duration=60s,filename=hardwood.jfr
风险限:Alpha 版无谓推(predicate pushdown),平坦 schema 最优;嵌套 / 大文件 (>2GB / 文件) 需多文件。回滚:引入 hardwood-parquet-java-compat 兼容 parquet-java API,无需改代码。
通过上述配置,Hardwood 在无额外 deps 下实现 parquet-java 数倍吞吐,适用于 ETL、ML 特征工程等场景。
资料来源:
- Hardwood 介绍博客:“Hardwood 采用页级并行、适应性页预取和跨文件预取。”
- GitHub 仓库,含基准测试与 ROADMAP。