Hardwood 1.0:Java 读取 Parquet 的轻量新选择

2026-07-03 45 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

Hardwood 到达 1.0,是 Java 生态里处理 Apache Parquet 文件的一个值得关注的信号。这个由 Gunnar Morling 发起的项目,目标很直接:用多线程读取和零强制外部依赖,给开发者一个比 Apache Parquet Java 实现更轻、更容易嵌入的选择。当前版本只支持读取,写入能力还在后续版本计划中。

为什么“零强制依赖”很有吸引力

Parquet 常出现在数据湖、批处理、分析平台和离线导入链路里。传统 Java 项目接入 Parquet 时,经常不只是引入一个 JAR,而是带上一串传递依赖。对平台型服务、命令行工具、嵌入式组件来说,这会带来几个现实问题:

  • 依赖冲突:例如不同模块拉入不同版本的压缩、Hadoop 或 Arrow 相关库。
  • 启动和打包复杂度:fat jar、native image、serverless 场景都更敏感。
  • 安全扫描噪声:传递依赖越多,CVE 和许可证审计越难控。

Hardwood 的“zero mandatory external dependencies”并不等于你永远不需要任何配套库,而是它把基础读取能力做得更独立。这个设计对只想读取 Parquet、抽取字段、做轻量转换的 Java 服务尤其友好。

多线程读取适合什么场景

Parquet 是列式格式,天然适合按列读取、跳过不需要的数据块。Hardwood 强调多线程处理,意味着它的目标不是只做一个最小解析器,而是想把现代 CPU 的并行能力用起来。

比较适合优先评估的场景包括:

  • 后端服务读取上传的 Parquet 文件,转换成内部行模型。
  • 数据质量检查工具扫描目录中的 Parquet 文件。
  • 批处理任务从对象存储下载文件后做本地解析。
  • 不想引入完整 Hadoop/Parquet Java 依赖树的小型 CLI。

边界也要明确:当前 Hardwood 1.0 只支持读取。如果你的链路需要生成 Parquet 文件、重写 schema、追加分区文件,暂时还不能只靠 Hardwood 完成。

可以这样做一次低风险试用

下面这个例子不是 Hardwood 官方 API 示例,因为摘要没有给出具体类名和 Maven 坐标。它是一个可复制的评估骨架:你只需要把依赖坐标和读取调用替换成 Hardwood 当前发布版本中的真实 API,就能在自己的项目里验证依赖体积、读取耗时和线程配置。

创建一个临时 Gradle 项目:

mkdir hardwood-parquet-spike
cd hardwood-parquet-spike
cat > settings.gradle <<'EOF'
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories { mavenCentral() }
}
rootProject.name = 'hardwood-parquet-spike'
EOF

cat > build.gradle <<'EOF'
plugins {
    id 'java'
    id 'application'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = 'demo.ParquetReadSpike'
}

dependencies {
    // 替换为 Hardwood 1.x 的实际 Maven 坐标。
    // implementation '...:hardwood:1.0.0'
}
EOF

mkdir -p src/main/java/demo
cat > src/main/java/demo/ParquetReadSpike.java <<'EOF'
package demo;

import java.nio.file.Path;

public class ParquetReadSpike {
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: ./gradlew run --args '<file.parquet>'");
            System.exit(2);
        }

        Path parquetFile = Path.of(args[0]);
        long started = System.nanoTime();

        // 假设性适配层:把这里替换成 Hardwood 当前版本的真实读取 API。
        // 建议保留这一层,不要让业务代码直接散落具体库调用。
        long rows = new HardwoodParquetReader().countRows(parquetFile);

        long elapsedMs = (System.nanoTime() - started) / 1_000_000;
        System.out.printf("file=%s rows=%d elapsedMs=%d%n", parquetFile, rows, elapsedMs);
    }

    static final class HardwoodParquetReader {
        long countRows(Path file) {
            throw new UnsupportedOperationException(
                "Replace this adapter with Hardwood's real read API"
            );
        }
    }
}
EOF

你可以先不写业务逻辑,只检查依赖树是否符合预期:

./gradlew dependencies --configuration runtimeClasspath

接入真实 API 后,再用同一个 Parquet 文件对比旧实现和 Hardwood:

./gradlew run --args '/path/to/sample.parquet'

评估时建议记录三类数据:读取耗时、峰值内存、运行时依赖数量。Hardwood 的卖点之一是依赖简单,所以不要只看吞吐量,也要看打包产物和依赖冲突是否真的减少。

接入时保留一层适配器

即使 Hardwood 到了 1.0,也建议用接口隔离它和业务代码。原因很简单:写入支持还没到,未来 API 可能围绕 writer、schema 或并发配置继续扩展。把读取逻辑包在一个小接口里,后续切换或双跑更容易。

可以这样设计:

package demo;

import java.nio.file.Path;
import java.util.stream.Stream;

public interface ParquetRowsReader<T> {
    Stream<T> read(Path file) throws Exception;
}

业务代码依赖 ParquetRowsReader<T>,实现类里再调用 Hardwood。这样你可以在测试环境里同时保留 Apache Parquet Java 实现和 Hardwood 实现,用同一批样本文件比较结果一致性。

采用建议:从读取链路开始,不要急着全量替换

Hardwood 1.0 最适合从“只读”的小入口开始试用,比如导入、校验、预览、统计这类任务。它的价值点很清楚:多线程读取、依赖更轻、嵌入更简单。但它当前不是完整 Parquet 工具箱,尤其还不覆盖写入。

落地前可以按这个清单走:

  • 确认当前链路只需要读取 Parquet。
  • 选 3 到 5 个真实样本文件,覆盖不同 schema 和压缩方式。
  • 对比旧实现与 Hardwood 的行数、字段值、异常行为。
  • 检查 runtime classpath,确认依赖减少是否真实发生。
  • 把 Hardwood 调用封装在适配器后面,避免业务层直接绑定库 API。

如果你的系统被 Parquet Java 的依赖树拖得很重,Hardwood 值得认真试一次;如果你需要成熟的读写全套能力,现在更合理的策略是把它放进只读路径中渐进验证。


相关推荐