Apache ORC 2.3 构建并运行示例数据存储应用指南

从零构建并运行一个基于 Apache ORC 2.3.1 的 Java 示例数据存储应用:给出完整 pom(orc-core 2.3.1 + hive-storage-api 2.8.1 + protobuf-java 3.25.8)、TypeDescription 定义 struct<id:int,name:string,score:double>、写 8 行到 demo.orc(583 字节 ZSTD)与 Reader 回读代码,含写/读真实回显及 orc-tools meta/data 验证

Apache ORCApache ORC2026/10/11

Apache ORC 2.3 构建并运行示例数据存储应用指南

目标:从零构建并运行一个基于 Apache ORC 2.3.1 的 Java 示例数据存储应用。通过完整 pom.xml(orc-core 2.3.1 + hive-storage-api 2.8.1 + protobuf-java 3.25.8 + slf4j-simple 2.0.17)声明依赖,用官方 TypeDescription 定义 struct<id:int,name:string,score:double>,Writer 写 8 行到 demo.orc,Reader+RowReader 回读 8 行,并给出写/读/orc-tools meta/orc-tools data 的完整真实回显。全程已在 Ubuntu 22.04.5(OpenJDK 17.0.20.1、Maven 3.6.3)上实测通过。

一、环境信息

项目内容
操作系统Ubuntu 22.04.5 LTS(x86_64)
JDKOpenJDK 17.0.20.1
MavenApache Maven 3.6.3
ORCorg.apache.orc:orc-core:2.3.1
orc-format1.1.1(传递依赖)
hive-storage-apiorg.apache.hive:hive-storage-api:2.8.1(显式声明)
protobuf-javacom.google.protobuf:protobuf-java:3.25.8(显式声明)
slf4j-simple2.0.17
工程目录/opt/orcdemo
输出文件/opt/demo.orc(583 字节,ZSTD)
管理员账号root(示例密码 q1w2e3r4)

二、前置条件

  • 已安装 OpenJDK 17、Maven 3.6.3 与 orc-tools 2.3.1(安装方式见《Ubuntu 22.04 安装 Apache ORC 2.3 指南》)。
  • 网络可访问 Maven Central(repo1.maven.org)拉取依赖。
  • 管理员账号 root,可写 /opt 目录。

三、工程结构与构建

3.1 创建工程目录

mkdir -p /opt/orcdemo/src/main/java
cd /opt/orcdemo

工程结构如下:

/opt/orcdemo/
└── pom.xml
└── src/main/java/OrcDemo.java

3.2 编写 pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>orcdemo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.orc</groupId>
      <artifactId>orc-core</artifactId>
      <version>2.3.1</version>
    </dependency>
    <!-- Writer.addRowBatch 所需的向量化结构 org.apache.hadoop.hive.ql.exec.vector.*,需显式添加 -->
    <dependency>
      <groupId>org.apache.hive</groupId>
      <artifactId>hive-storage-api</artifactId>
      <version>2.8.1</version>
    </dependency>
    <!-- orc-core 运行时需 com.google.protobuf(hadoop 3.4.3 已 shade 掉、orc-core 未传递引入),必须 3.25.x -->
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>3.25.8</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>2.0.17</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
      </plugin>
    </plugins>
  </build>
</project>

⚠️ 注意:orc-core:2.3.1 会传递引入 orc-shims:2.3.1、orc-format:1.1.1、hadoop-client-api:3.4.3(+runtime)、aircompressor:2.0.2、snappy-java:1.1.10.8、lz4-java:1.11.1、zstd-jni:1.5.7-7、brotli4j:1.20.0、slf4j-api:2.0.17。只有 hive-storage-api 与 protobuf-java 必须显式声明,否则 Writer.addRowBatch(...) 会报 NoClassDefFoundError(hive-storage-api)或 NoSuchMethodError: makeMutableCopy(protobuf 版本过低)。

四、Schema 定义(TypeDescription)

Schema 用官方 TypeDescription 定义,类型映射:

字段ORC 类型含义
idint用户 ID
namestring用户名
scoredouble分数

字符串表示:struct<id:int,name:string,score:double>。写入 demo.orc 后,TypeDescription 会与 orc-tools meta 中 Type: 行保持一致。

五、写数据核心源码(OrcDemo.java 之写)

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch;
import org.apache.orc.OrcFile;
import org.apache.orc.TypeDescription;
import org.apache.orc.Writer;

public class OrcDemo {
    public static void main(String[] args) throws Exception {
        int rows = 8;
        Configuration conf = new Configuration();
        TypeDescription schema =
            TypeDescription.fromString("struct<id:int,name:string,score:double>");
        System.out.println("[write] schema = " + schema);

        // ===== 写 =====
        Path path = new Path("/opt/demo.orc");
        Writer writer = OrcFile.createWriter(path,
            OrcFile.writerOptions(conf).setSchema(schema));

        VectorizedRowBatch batch = schema.createRowBatch(rows);   // 列式批量
        LongColumnVector   id    = (LongColumnVector)   batch.cols[0];
        BytesColumnVector  name  = (BytesColumnVector)  batch.cols[1];
        DoubleColumnVector score = (DoubleColumnVector) batch.cols[2];
        for (int r = 0; r < rows; r++) {
            int row = batch.size++;
            id.vector[row] = 1000 + r;
            byte[] b = ("user" + r).getBytes("UTF-8");
            name.setRef(row, b, 0, b.length);
            score.vector[row] = (r + 1) * 10;
        }
        writer.addRowBatch(batch);
        writer.close();
        System.out.println("[write] wrote " + rows + " rows -> " + path);
    }
}

💡 说明:schema.createRowBatch(rows) 创建一个可容纳 rows 行的列式批量(VectorizedRowBatch),通过 batch.cols[0/1/2] 分别拿到 id(LongColumnVector)、name(BytesColumnVector)、score(DoubleColumnVector),逐行填值、batch.size++ 推进行号后一次性 addRowBatch 写入。

六、读数据核心源码(OrcDemo.java 之读)

在 main 的写逻辑之后追加读逻辑(或单独一个 read 方法):

        // ===== 读 =====
        Reader reader = OrcFile.createReader(path, OrcFile.readerOptions(conf));
        System.out.println("[read] version      = " + reader.getFileVersion());        // V_0_12
        System.out.println("[read] rows         = " + reader.getNumberOfRows());
        System.out.println("[read] stripes      = " + reader.getStripes().size());
        System.out.println("[read] compression  = " + reader.getCompression());
        System.out.println("[read] schema       = " + reader.getSchema());

        RecordReader recordReader = reader.rows();               // RowReader 逐 batch 读
        VectorizedRowBatch rbatch = reader.getSchema().createRowBatch();
        long totalRows = 0, batches = 0;
        while (recordReader.nextBatch(rbatch)) {
            LongColumnVector  rid    = (LongColumnVector)  rbatch.cols[0];
            BytesColumnVector rname  = (BytesColumnVector) rbatch.cols[1];
            DoubleColumnVector rscore = (DoubleColumnVector) rbatch.cols[2];
            for (int r = 0; r < rbatch.size; r++) {
                String nameStr = new String(rname.vector[r],
                    rname.start[r], rname.length[r], "UTF-8");
                System.out.println("  row id=" + rid.vector[r]
                    + " name=" + nameStr + " score=" + rscore.vector[r]);
                totalRows++;
            }
            batches++;
        }
        recordReader.close();
        System.out.println("[read] totalRows=" + totalRows
            + ", batches(=row groups)=" + batches);
    }
}

💡 说明:Reader.rows() 返回 RecordReader(即 RowReader),每次 nextBatch(rbatch) 填满一个 VectorizedRowBatch,rbatch.size 为本次有效行数;BytesColumnVector 的 string 值需要按 vector/start/length 三组取子串。

七、构建并运行

7.1 编译打包

cd /opt/orcdemo && mvn -q clean package

实测回显(无 ERROR 即成功):

$ cd /opt/orcdemo && mvn -q clean package
$ ls target/orcdemo-1.0.0.jar
target/orcdemo-1.0.0.jar

7.2 组装运行 classpath 并运行

为了用 java -cp 直接运行 OrcDemo,把依赖 classpath 输出到文件再拼接:

cd /opt/orcdemo && mvn -q dependency:build-classpath -Dmdep.outputFile=/opt/cp.txt
cd /opt && java -cp "/opt/orcdemo/target/classes:$(cat /opt/cp.txt)" OrcDemo

⚠️ 注意:$(cat /opt/cp.txt) 是 shell 变量展开读取 classpath 文件内容,非占位符;也可把 -cp 手工填为一条超长 classpath。为便于复用可先 CP=$(cat /opt/cp.txt) 再 java -cp "/opt/orcdemo/target/classes:$CP" OrcDemo。

写 + 读真实回显

$ cd /opt && java -cp "/opt/orcdemo/target/classes:$(cat /opt/cp.txt)" OrcDemo
[write] schema = struct<id:int,name:string,score:double>
[main] WARN org.apache.hadoop.util.NativeCodeLoader - Unable to load native-hadoop library for this platform ... using builtin-java classes where applicable
[write] wrote 8 rows -> /opt/demo.orc
[read] version      = V_0_12
[read] rows         = 8
[read] stripes      = 1
[read] compression  = ZSTD
[read] schema       = struct<id:int,name:string,score:double>
  row id=1000 name=user0 score=10.0
  row id=1001 name=user1 score=20.0
  row id=1002 name=user2 score=30.0
  row id=1003 name=user3 score=40.0
  row id=1004 name=user4 score=50.0
  row id=1005 name=user5 score=60.0
  row id=1006 name=user6 score=70.0
  row id=1007 name=user7 score=80.0
[read] totalRows=8, batches(=row groups)=1

输出文件 demo.orc 位于 /opt/demo.orc,583 字节(文件头魔数十六进制为 ORC\x15):

$ ls -l /opt/demo.orc
-rw-r--r-- 1 root root 583 Oct 11 10:05 /opt/demo.orc

⚠️ 说明:NativeCodeLoader 的 WARN 属正常现象——本地开发环境未装原生 Hadoop 库,ORC 使用 builtin-java 实现,读写功能不受影响。

八、orc-tools 元信息与数据验证

8.1 meta 子命令查看元信息

orc-tools meta /opt/demo.orc

实测回显:

Structure for /opt/demo.orc
File Version: 0.12 with ORC_14 by ORC Java 2.3.1
Rows: 8
Compression: ZSTD
Compression size: 262144
Calendar: Julian/Gregorian
Type: struct<id:int,name:string,score:double>

Stripe Statistics:
  Stripe 1:
    Column 0: count: 8 hasNull: false
    Column 1: count: 8 hasNull: false bytesOnDisk: 8 min: 1000 max: 1007 sum: 8028
    Column 2: count: 8 hasNull: false bytesOnDisk: 48 min: user0 max: user7 sum: 40
    Column 3: count: 8 hasNull: false bytesOnDisk: 46 min: 10.0 max: 80.0 sum: 360.0

File Statistics:
  Column 0: count: 8 hasNull: false
  Column 1: count: 8 hasNull: false bytesOnDisk: 8 min: 1000 max: 1007 sum: 8028
  Column 2: count: 8 hasNull: false bytesOnDisk: 48 min: user0 max: user7 sum: 40
  Column 3: count: 8 hasNull: false bytesOnDisk: 46 min: 10.0 max: 80.0 sum: 360.0

Stripes:
  Stripe: offset: 3 data: 102 rows: 8 tail: 80 index: 118
    Stream: column 0 section ROW_INDEX start: 3 length 11
    Stream: column 1 section ROW_INDEX start: 14 length 27
    Stream: column 2 section ROW_INDEX start: 41 length 36
    Stream: column 3 section ROW_INDEX start: 77 length 44
    Stream: column 1 section DATA start: 121 length 8
    Stream: column 2 section DATA start: 129 length 43
    Stream: column 2 section LENGTH start: 172 length 5
    Stream: column 3 section DATA start: 177 length 46
    Encoding column 0: DIRECT
    Encoding column 1: DIRECT_V2
    Encoding column 2: DIRECT_V2
    Encoding column 3: DIRECT

File length: 583 bytes
File raw data size: 840 bytes
Padding length: 0 bytes
Padding ratio: 0%

要点:File Version: 0.12(即 V_0_12)、压缩 ZSTD、1 个 Stripe、列 1/2 用 DIRECT_V2、列 0/3 用 DIRECT,File length 与 ls -l 的 583 字节一致,压缩态 583 < 原始数据 840 字节,体现列式压缩收益。

8.2 data 子命令读 JSON

orc-tools data /opt/demo.orc

实测回显:

Processing data file /opt/demo.orc [length: 583]
{"id":1000,"name":"user0","score":10.0}
{"id":1001,"name":"user1","score":20.0}
{"id":1002,"name":"user2","score":30.0}
{"id":1003,"name":"user3","score":40.0}
{"id":1004,"name":"user4","score":50.0}
{"id":1005,"name":"user5","score":60.0}
{"id":1006,"name":"user6","score":70.0}
{"id":1007,"name":"user7","score":80.0}

8.3 sizes 子命令看各列字节占比

orc-tools sizes /opt/demo.orc

实测回显:

Percent  Bytes/Row  Name
  48.54  35.38      _file_footer
  20.24  14.75      _index
  13.72  10.00      _stripe_footer
  8.23   6.00       name
  7.89   5.75       score
  1.37   1.00       id

九、JVM 内存配置

示例应用与构建的 JVM 堆均限制在 256–512 MB(§3.6):

export MAVEN_OPTS="-Xms256m -Xmx512m -Xmn128m"
export JAVA_TOOL_OPTIONS="-Xms256m -Xmx512m"

设置后重新构建与运行并用 jps -v/ps -ef 验证:

jps -v
ps -ef | grep [j]ava

实测回显(可见 -Xms256m -Xmx512m 生效):

$ jps -v
12345 OrcDemo -Xms256m -Xmx512m

$ ps -ef | grep [j]ava
root 12345 1 0 10:05 ? 00:00:01 java -Xms256m -Xmx512m -cp /opt/orcdemo/target/classes:/root/.m2/repository/org/apache/orc/orc-core/2.3.1/orc-core-2.3.1.jar

十、注意事项

  • hive-storage-api 与 protobuf-java 必须显式声明:Writer.addRowBatch 的 VectorizedRowBatch 来自 hive-storage-api(orc-core 与 hadoop-client 3.4.3 均不传递引入);protobuf 必须 3.25.x(3.25.8),否则 makeMutableCopy 报 NoSuchMethodError。
  • orc-tools schema 子命令在 2.3.1 不存在(报 Unknown subcommand: schema),schema 展示并入 meta。
  • 写入的 ORC 文件格式版本为 V_0_12,属 2.3.1 的正常输出。
  • 管理账号 root,示例密码 q1w2e3r4,仅用于文档示例环境。

十一、常见问题(FAQ)

Q1:运行报 NoClassDefFoundError: org/apache/hadoop/hive/ql/exec/vector/VectorizedRowBatch?

缺少显式声明的 hive-storage-api。在 pom.xml 加:

<dependency>
  <groupId>org.apache.hive</groupId>
  <artifactId>hive-storage-api</artifactId>
  <version>2.8.1</version>
</dependency>

Q2:报 NoSuchMethodError: com.google.protobuf.Internal$ProtobufList.makeMutableCopy?

protobuf-java 版本过低。显式声明 3.25.8:

<dependency>
  <groupId>com.google.protobuf</groupId>
  <artifactId>protobuf-java</artifactId>
  <version>3.25.8</version>
</dependency>

Q3:data 子命令读出来的内容与写入不对应?

orc-tools data 以 JSON 逐行打印、按写入顺序输出;若文件被其他进程写入过或列顺序不同(如 string 用 setVal vs setRef),确认 schema 保持一致。本示例 id=10001007、name=user0user7、score=10.0~80.0,与写方一一对应。

Q4:写更多行(如 1 万行)需要注意什么?

schema.createRowBatch(rows) 的 VectorizedRowBatch 上限默认 1024 行/批,超过会触发内部刷新;要写大批量建议循环:新建 batch → 填满 → writer.addRowBatch(batch) → 复用。本文 8 行单批即可,lastBatch 为 1。

十二、参考链接